2

在 Visual C++ 中,如果我有一个值为 inf 的 double,我使用流输出它:

double myval = std::numeric_limits<double>::infinity();
std::ostringstream msg;
msg << "This is infinite: " << myval;

结果是“1.#INF”。

有没有简单的方法让它简单地打印“inf”或“INF”?这个字符串出现在随后将被解析的文本中,额外的字符给我们带来了问题。

我想过为 double 重载流运算符,但 double 是内置类型。

我承认我无法确切地弄清楚如何寻找基本问题的答案......

谢谢!

4

1 回答 1

1

这是可能的,但有点不重要,正确的方法是相当模糊的。

顺便说一句,我会注意到,当你做一些事情时,msg << myval;只有一个操作数必须是用户定义的类型,这里就是这种情况(即使没有定义它,anostringstream仍然是正式的用户-定义类型)。不过,这或多或少无关紧要。现有的过载operator<<将正常工作;你不需要提供你自己的。

我认为流是“媒人”。您有一个流缓冲区来处理实际的 I/O,还有一个语言环境来处理格式化。这样一想,解决方案就变得相当清楚了:由于您要更改的是格式,并且格式是由语言环境处理的,因此您需要更改语言环境。

然而,语言环境实际上是一个异构集合。具体来说,它是类的集合facet。在这种情况下,我们关心的切面就是num_put切面。num_put facet 类具有do_put各种类型的虚拟成员函数。在这种情况下,我们关心的是double

template <class charT, class OutputIterator = std::ostreambuf_iterator<charT> >
class num_put : public std::num_put<charT, OutputIterator> {
public:
    virtual iter_type do_put(iter_type i, 
                             std::ios_base& b, 
                             char_type fill, 
                             double v) const 
    {
        if (v == std::numeric_limits<double>().infinity()) {
            static const char inf[]="inf";      
            std::copy(std::begin(inf), std::end(inf), i);
        }
        else {
            std::ostringstream temp;
            temp << v;
            std::copy(temp.str().begin(), temp.str().end(), i);
        }
        return i;           
    }
};

要使用它,您需要为相关流注入包含该方面的语言环境:

int main() {
    char *d="0";

    std::locale loc(std::locale::classic(), new num_put<char>);
    std::cout.imbue(loc);

    std::cout << 1.0/atoi(d);
    return 0;
}

但是,我应该补充一点,这很快就被拍打在一起了,而且测试非常少。它适用于测试用例,也可能适用于其他狭窄的流。猜测一下,它可能需要更多的工作才能在宽流中正常工作。

于 2012-09-29T23:44:49.257 回答