便携式打印双精度指数到 C++ iostreams

发布于 2025-01-04 11:45:09 字数 670 浏览 4 评论 0原文

我想以可移植方式(GCC、clang、MSVC++)将双精度值打印到 std::cout,以便所有平台上的输出都相同。

我的指数格式有问题。 以下程序

#include <iostream>
int main()
{
    std::cout << 0.1e-7 << std::endl;
    return 0;
}

在 GCC 中具有此输出:

1e-08

在 MSVC 中具有以下输出

1e-008

我怎样才能使两个输出相同?

如果这是一个愚蠢的问题,我很抱歉,但到目前为止我还没有找到答案。 所有格式似乎都是围绕尾数之前所有内容的格式演变的...

编辑:GCC 的输出是 1e-08 而不是 1e-8 (如最初所述),所以它符合。抱歉造成混乱。

编辑2:实际上按照迪特马尔的评论将“尾数”重命名为“指数”。 维基百科上还有一个关于尾数与有效位数的部分

I want to print a double value to std::cout portably (GCC, clang, MSVC++) such that the output is the same on all platforms.

I have a problem with the formatting of the exponent.
The following program

#include <iostream>
int main()
{
    std::cout << 0.1e-7 << std::endl;
    return 0;
}

Has this output with GCC:

1e-08

and the following output with MSVC

1e-008

How can I make both outputs the same?

I'm sorry if this is a dumb question but I have not found an answer so far.
All formatting seems to evolve around the formatting of everything before the mantissa...

EDIT: The output of GCC is 1e-08 not 1e-8 (as originally stated) so it is conforming. Sorry for the confusion.

EDIT2: Actually renamed "mantissa" to "exponent" following Dietmar's remark. There also is a section on Wikipedia on mantissa vs. significant.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

痕至 2025-01-11 11:45:09

没有操纵器控制指数的格式(我假设您指的是指数而不是尾数;此外,用于尾数的“官方”名称​​有意义)。更糟糕的是,我在 C 标准中看不到任何限制指数格式的规则。我意识到这是关于 C++ 的,但出于格式细节的目的,C++ 标准指的是 C 标准。

我知道的唯一方法是使用自己的 std::num_put 方面,根据需要格式化值。然后,这个方面将被放入 std::locale 中,而该 imbue() 又被 imbue() 编辑到 std::cout 中。潜在的实现可以使用默认的 std::num_put方面(或不幸的是,可能更简单的 snprintf())来格式化浮点数,然后从指数中去除前导零。

There is no manipulator controlling the formatting of the exponent (I assume you mean the exponent rather than the mantissa; also, the "official" name used for the mantissa is significant). To make matters worse, I can't see any rule in the C standard which restricts the formatting of the exponent. I realize that this is about C++ but for the purpose of the formatting details, the C++ standard refers to the C standard.

The only approach I'm aware of is to use an own std::num_put<char> facet which formats the values as desired. This facet would then be put into a std::locale which in turn is imbue()ed into std::cout. A potential implementation could use the default std::num_put<char> facet (or snprintf() which is, unfortunately, probably simpler) to format the floating point number and then strip leading zeros from the exponent.

娇俏 2025-01-11 11:45:09

虽然 Dietmar 的答案是干净的,而且可能是唯一真正便携的答案,但我意外地发现了一个快速而肮脏的答案:
MSVC 提供了 _set_output_format 函数,您可以用于切换到“将指数打印为两位数”。

可以在您的 main() 函数中实例化以下 RAII 类,为您提供与 GCC、CLANG 和 MSVC 相同的行为。

class ScientificNotationExponentOutputNormalizer
{
public:
    unsigned _oldExponentFormat;

    ScientificNotationExponentOutputNormalizer() : _oldExponentFormat(0)
    {
#ifdef _MSC_VER
        // Set scientific format to print two places.
        unsigned _oldExponentFormat = _set_output_format(_TWO_DIGIT_EXPONENT);
#endif
    }

    ~ScientificNotationExponentOutputNormalizer()
    {
#ifdef _MSC_VER
        // Enable old exponent format.
        _set_output_format(_oldExponentFormat);
#endif
    }
};

While Dietmar's answer is the clean and probably only really portable answer, I accidentally found a quick-and-dirty answer:
MSVC provides the _set_output_format function which you can use to switch to "print exponent as two digits".

The following RAII class can be instantiated in your main() function to give you the same behaviour of GCC, CLANG and MSVC.

class ScientificNotationExponentOutputNormalizer
{
public:
    unsigned _oldExponentFormat;

    ScientificNotationExponentOutputNormalizer() : _oldExponentFormat(0)
    {
#ifdef _MSC_VER
        // Set scientific format to print two places.
        unsigned _oldExponentFormat = _set_output_format(_TWO_DIGIT_EXPONENT);
#endif
    }

    ~ScientificNotationExponentOutputNormalizer()
    {
#ifdef _MSC_VER
        // Enable old exponent format.
        _set_output_format(_oldExponentFormat);
#endif
    }
};
旧夏天 2025-01-11 11:45:09

问题是 Visual C++ 没有遵循 C99 标准。在 Visual C++ 2015 中,_set_output_format 已被删除,因为编译器现在遵循标准:

%e%E 格式说明符将浮点数格式化为十进制尾数和指数。在某些情况下,%g%G 格式说明符也以这种形式格式化数字。在以前的版本中,CRT 总是生成具有三位数指数的字符串。例如,printf("%e\n", 1.0)将打印1.000000e+000这是不正确的:C 要求如果指数只能使用一位或两位数字表示,则只打印两位数字

在 Visual Studio 2005 中添加了全局一致性开关:_set_output_format。程序可以使用参数_TWO_DIGIT_EXPONENT调用此函数,以启用一致的指数打印。 默认行为已更改为符合标准的指数打印模式

请参阅 Visual C++ 2015 中的重大更改 。对于旧版本,请参阅@Manuel 的答案。

仅供参考,在 C99 标准中,我们可以阅读:

e,E

表示浮点数的双精度参数以 [-]d.ddd e(+-)dd 样式进行转换,其中小数点前有一位数字(如果参数非零,则该数字非零)字符及其后面的位数等于精度;如果精度缺失,则取6;如果精度为零且未指定 # 标志,则不出现小数点字符。该值将四舍五入到适当的位数。 E 转换说明符生成一个数字,其中 E 而不是 e 引入指数。 指数始终包含至少两位数字,并且仅包含表示指数所需的更多数字。如果该值为零,则指数为零。表示无穷大或 NaN 的双参数以 f 或 F 转换说明符的样式进行转换。

与 C90 相比,这是一个差异,C90 没有给出任何有关所需指数长度的指示。

请注意,最近的 Visual C++ 更改还涉及如何打印 naninf 等。

The problem is that Visual C++ was not following the C99 standard. In Visual C++ 2015, _set_output_format was removed since the compiler now follows the standard:

The %e and %E format specifiers format a floating point number as a decimal mantissa and exponent. The %g and %G format specifiers also format numbers in this form in some cases. In previous versions, the CRT would always generate strings with three-digit exponents. For example, printf("%e\n", 1.0)would print 1.000000e+000. This was incorrect: C requires that if the exponent is representable using only one or two digits, then only two digits are to be printed.

In Visual Studio 2005 a global conformance switch was added: _set_output_format. A program could call this function with the argument _TWO_DIGIT_EXPONENT, to enable conforming exponent printing. The default behavior has been changed to the standards-conforming exponent printing mode.

See Breaking Changes in Visual C++ 2015. For older versions, see @Manuel's answer.

FYI, in the C99 standard, we can read:

e,E

A double argument representing a floating-point number is converted in the style [-]d.ddd e(+-)dd, where there is one digit (which is nonzero if the argument is nonzero) before the decimal-point character and the number of digits after it is equal to the precision; if the precision is missing, it is taken as 6; if the precision is zero and the # flag is not specified, no decimal-point character appears. The value is rounded to the appropriate number of digits. The E conversion specifier produces a number with E instead of e introducing the exponent. The exponent always contains at least two digits, and only as many more digits as necessary to represent the exponent. If the value is zero, the exponent is zero. A double argument representing an infinity or NaN is converted in the style of an f or F conversion specifier.

This is a difference compared to C90 that did not give any indication concerning the required exponent length.

Note that the recent Visual C++ changes also concern how to print nan, inf etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文