升级到 Visual Studio 2013 后,我注意到一些基本数学函数的工作方式有所不同,例如 <cmath>
中的 sin()|或 <math.h>
.这可能是一个很好的改变,但一些测试表明其中一个主要的数学规则被打破了。这是测试函数:
void Test()
{
double d_angle = M_PI_2 / 2;
auto sin_val = sin(d_angle);
auto cos_val = cos(d_angle);
auto sum_of_squares = sin_val*sin_val + cos_val*cos_val;
Assert::AreEqual(1.0, sum_of_squares, 1e-16);
}
此测试在 VS2013 中失败,但在 VS2012 中通过。
如何处理这种情况并得到 VS2012 的结果?
最佳答案
基本问题是 float 学不是真正的数学(或者至少不是实数集上的数学)。 IEEE 754 double float 具有 53 位尾数。您 Assert::AreEqual
指定 sum_of_squares
的尾数应为 1 位,后跟 52 个零位 ±1e-16。
最低有效位的错误将为 1 ± 2-52 = 1 ± 2.220446e-16,因此如果最低有效位的舍入方式不同,您的 Assert::AreEqual
将失败。本质上,您是说答案必须以 0 错误计算。
显然,VS2012 得到的答案完全正确,而现在 VS2013 却没有。这可能有几个可能的原因:
VS2013 不执行符合 IEEE 754 的数学运算。过去发生过数学错误,但很少见。如果您得到的结果在最低有效位置的偏差小于一位,则不太可能是编译器或数学库错误。
VS2013 编译器重新排序浮点运算,这改变了部分结果的舍入方式。如果结果会改变,编译器不应该重新排序浮点操作,但大多数都有优化标志,允许重新排序,这是数学允许的,当它会提高性能时(例如,gcc 中的 -ffast-math)。这可能不是问题,除非您一直在试验编译器选项。
VS2013 编译器优化器以不同的方式决定要使用的 x87 和 SSE/SSE2 指令的组合,或者它改变了寄存器分配的方式。 x87 FPU 有一个 80 位浮点寄存器,而 SSE2 有 64 位浮点寄存器。如果编译器在 x87 浮点寄存器中保留计算并仅存储最终结果,则该结果的舍入方式可能与使用 SSE2 计算的结果不同,因为用于中间值的尾数较长。
由于寄存器压力,编译器强制将中间结果存入内存。结果,一个或多个中间值被截断为 64 位 float 。对于这么短的程序,这是不可能的。
如果您将 delta
参数提升到 Assert::AreEqual
直到 2.23e-16 并且它通过了,那么区别在于最低有效位被四舍五入。
根据VS2013 documentation for the /arch
compiler option :
The optimizer chooses when and how to use the SSE and SSE2 instructions when /arch is specified. It uses SSE and SSE2 instructions for some scalar floating-point computations when it determines that it is faster to use the SSE/SSE2 instructions and registers instead of the x87 floating-point register stack. As a result, your code may actually use a mixture of both x87 and SSE/SSE2 for floating-point computations. Also, with /arch:SSE2, SSE2 instructions can be used for some 64-bit integer operations.
和
Because the x86 compiler generates code that uses SSE2 instructions by default, you must specify /arch:IA32 to disable generation of SSE and SSE2 instructions for x86 processors.
VS2012 文档说了几乎相同的事情。两者都不清楚编译器何时选择 x87 或 SSE/SSE2。
查看反汇编会告诉您编译器正在使用哪些指令,以及结果是否写入内存。
关于c++ - VS2013 中破坏的毕达哥拉斯三角恒等式规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32323565/