【问题标题】:sprintf formatting problem for doubles with high precision高精度双打的sprintf格式问题
【发布时间】:2020-06-30 15:42:38
【问题描述】:

所以我最近升级了一个使用 Visual Studio 2012 - Windows XP (v110_xp) 平台工具集构建的旧 c++ 项目。在这个项目的代码中,发生了一些非常精确的双重计算,需要多达 20 个字符的精度。然后将这些双打保存到一个字符串中,并使用printf API 打印出来。以下是该项目中将发生的事情的示例:

        double testVal = 123.456789;

        // do some calculations on testVal

        char str[100] = { 0 };

        sprintf(str, "%.20le", testVal);

在这个操作之后 str = "1.23456789000...000e+02",这是预期的结果。

但是,一旦我将项目更新为与 Visual Studio 2019 兼容,使用 Visual Studio 2019 (v142) 平台工具集和 c++ 17,上述代码会为 str 生成不同的输出。 在调用sprintf 将值格式化为字符串str = "1.23456789000...556e+02" 之后。这个问题并不局限于这个值,还有更多的问题。例如,sprintf 格式后的"2234332.434322" 的起始值之一更改为"2.23433324343219995499e+07"

从我使用“l”格式代码阅读的所有文档中,它应该是将长双精度转换为字符串的正确字符。这种行为感觉就像教科书的 float->double 转换。

我尝试将项目浮点模型构建参数设置为精确、严格,然后快速查看这些选项中的任何一个是否有帮助,但它对问题没有影响。

有人知道为什么会这样吗?

【问题讨论】:

  • 您是担心为什么会发生变化,还是认为新的结果不正确?
  • IIRC 正确地,一个双精度数只有大约 17 位十进制数字,所以最后只有 3 个“垃圾”数字。
  • 我认为新的结果是不正确的。不同的值会在应用程序中进一步导致一些问题。
  • 您可能想阅读以下链接:Is floating point math broken?,What Every Computer Scientist Should Know About Floating-Point Arithmetic。此外,不能保证转换为字符串的浮点数或双精度数会正确往返返回到零错误的浮点数(尤其是不跨平台或编译器)。
  • @john 较新的 Visual Studio 实际上给出了更准确的值。当四舍五入到最接近的IEEE-754 double 时,123.456789 实际上是123.4567890000000005557012627832591533660888671875。不幸的是,x64(以及 Visual Studio)不提供更大的浮点类型。

标签: c++ visual-studio-2012 printf c++17 visual-studio-2019


【解决方案1】:

改用全新的 Ryu (https://github.com/ulfjack/ryu) 或 Grisu-Exact (https://github.com/jk-jeon/Grisu-Exact),它们比 sprintf 快​​得多,并且保证往返正确(以及更多),或者旧的 Double-Conversion (https://github.com/google/double-conversion) 比其他两个慢,但具有相同的保证,仍然比sprintf 快得多,并且经过实战考验。

(免责声明:我是 Grisu-Exact 的作者。)

我不确定您是否真的需要精确地打印出 20 位十进制数字,因为我个人很少遇到数字数量很重要的情况。如果拥有 20 位数字的唯一目的只是为了不丢失任何精度,那么上述库肯定会为您提供更好(和更短)的结果。如果由于某些原因位数必须精确为 20,那么 Ryu 仍然提供了这样一个功能(称为 Ryu-printf),它再次具有往返保证,并且比 sprintf 快得多。

编辑

要详细说明最后一句话,请注意,如果位数是固定的,通常不可能有往返保证,因为,如果这个固定数字太小,比如 3,那么就有没办法区分 0.123 和 0.1234。但是,20 足够大,因此始终保证真实值的最佳近似值(这是 Ryu-printf 产生的)是往返正确的。

【讨论】:

    猜你喜欢
    • 2014-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多