【发布时间】:2024-01-23 09:07:01
【问题描述】:
一开始是一个非常简单的问题,现在变成了一场噩梦。 C++ 中舍入值的行为会因某些因素而有所不同。
从下面一段简单的代码开始,您将在 2 个整数值中间的值传递给其他函数:
#include <stdio.h>
extern void print(double d);
extern void stream(double d);
extern void rounding(double d);
int main()
{
for (auto i=0;i<10;++i)
print(i+0.5);
printf("\n");
for (auto i=0;i<10;++i)
stream(i+0.5);
printf("\n");
for (auto i=0;i<10;++i)
rounding(i+0.5);
printf("\n");
}
这 3 个函数以 3 种不同的方式打印出值:使用 printf、使用 operator<< 和使用 round 函数:
#include <stdio.h>
#include <iomanip>
#include <iostream>
void print(double d)
{
printf("%.0lf ",d);
}
void stream(double d)
{
std::cout << std::fixed << std::setprecision(0) << d << " ";
}
void rounding(double d)
{
auto r = round(d);
printf("%.0lf ",r);
}
在所有这些情况下,我想打印出小数点后没有数字的值。
我得到了所有这些组合:
使用 Visual Studio 2015 或 2017 编译,在 Windows Server 2019 上运行,构建 14393:
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
使用 Visual Studio 2015 或 2017 编译,在 Windows 10 上运行,构建 19041:
1 2 3 4 5 6 7 8 9 10
0 2 2 4 4 6 6 8 8 10
1 2 3 4 5 6 7 8 9 10
如你所见,使用iostreams,operator<<突然决定从这个Windows版本开始使用Bankers Rounding。
使用 Visual Studio 2019 编译,在 Windows Server 2019 上运行,构建 14393:
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
使用 Visual Studio 2019 编译,在 Windows 10 上运行,构建 19041:
0 2 2 4 4 6 6 8 8 10
0 2 2 4 4 6 6 8 8 10
1 2 3 4 5 6 7 8 9 10
现在printf 函数也开始使用银行家四舍五入(使用 VS2015 或 VS2017 编译时不是这种情况)。
https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/fprintf-fprintf-l-fwprintf-fwprintf-l?view=msvc-160 页面指出,如果您在 legacy_stdio_float_rounding.obj 目标文件中链接,则可以恢复旧行为。事实上,如果你把它链接进去,你就会得到这个:
使用 Visual Studio 2019 编译,链接旧目标文件,在 Windows 10 上运行,内部版本 19041:
1 2 3 4 5 6 7 8 9 10
0 2 2 4 4 6 6 8 8 10
1 2 3 4 5 6 7 8 9 10
不幸的是,我似乎无法恢复流输出运算符的旧行为。
还有其他人也在为这个问题苦苦挣扎吗?
获得一致舍入的最佳解决方案是什么?
由于 C 标准清楚地指定了 round 函数的行为方式(根据值的符号向上舍入到 +/- 无穷大),因此 printf 和 operator<< 的行为方式似乎也是合乎逻辑的。那么我们是否应该告诉我们的开发人员在流式传输浮点值时不要使用输出运算符(更具体地说,std::fixed 和 std::setprecision)?
更糟糕的是:一些外部模块是用 JavaScript 编写的,它甚至有不同的舍入方式(总是向 +infinity 舍入,即使是负数)。 正如我在开头所说的那样:一开始是一个简单的问题,现在变成了一致性的噩梦。
你有遇到同样的问题吗?你是怎么处理的?
【问题讨论】:
-
为了投入工作,我正在运行 Windows 10,构建 19042 使用 Visual Studio 2019 编译,我得到 @ 987654343@ 所有 3 个案例的输出。
-
还有docs.microsoft.com/en-us/cpp/c-runtime-library/… "...在大多数情况下,产生的结果在正确舍入结果的+/-1 ulp之内,..."
-
行为是否还取决于 Visual Studio 中选择的 C++ 标准?默认标准是 C++14。
-
@Patrick 好吧,我不知道自己在做什么,所以在 vcxproj 中构建调试的默认选项:
/c /ZI /JMC /nologo /W3 /WX- /diagnostics:column /sdl /Od /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICODE /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /permissive- /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"x64\Debug\\" /Fd"x64\Debug\vc142.pdb" /Gd /TP /FC /errorReport:prompt- 老实说,我只知道什么就像其中 3 个开关一样。编译器是 19.28.29914。
标签: c++ visual-studio windows-10 printf rounding