高精度双精度的 Sprintf 格式化问题



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

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"格式代码的文档中,它应该是将长双精度转换为字符串的正确字符。不过,这种行为感觉就像教科书上的浮点>双精度转换。

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

有谁知道为什么会这样?

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

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

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

编辑

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

最新更新