c++ - VS2013 中破坏的毕达哥拉斯三角恒等式规则

标签 c++ visual-studio trigonometry math.h

升级到 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 却没有。这可能有几个可能的原因:

  1. VS2013 不执行符合 IEEE 754 的数学运算。过去发生过数学错误,但很少见。如果您得到的结果在最低有效位置的偏差小于一位,则不太可能是编译器或数学库错误。

  2. VS2013 编译器重新排序浮点运算,这改变了部分结果的舍入方式。如果结果会改变,编译器不应该重新排序浮点操作,但大多数都有优化标志,允许重新排序,这是数学允许的,当它会提高性能时(例如,gcc 中的 -ffast-math)。这可能不是问题,除非您一直在试验编译器选项。

  3. VS2013 编译器优化器以不同的方式决定要使用的 x87 和 SSE/SSE2 指令的组合,或者它改变了寄存器分配的方式。 x87 FPU 有一个 80 位浮点寄存器,而 SSE2 有 64 位浮点寄存器。如果编译器在 x87 浮点寄存器中保留计算并仅存储最终结果,则该结果的舍入方式可能与使用 SSE2 计算的结果不同,因为用于中间值的尾数较长。

  4. 由于寄存器压力,编译器强制将中间结果存入内存。结果,一个或多个中间值被截断为 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/

相关文章:

c++ - 切换功能在 C++ 中不起作用

c++ - C++中线程函数参数的问题

c++ - boost::bind 和类成员函数

sql-server - 要调试此项目,您必须启用 sql/clr

c# - 尝试将参数传递给 t4 模板时出错

ios - acos和cos的度数未给出正确的结果

c++ - 检索字符串的第一个字符并在 C++ 中按字母顺序比较它

javascript - 为什么在 visual studio 中使用 typescript 时需要在 default.htm 中包含多个 .js 文件?

python - 在 Numpy 中从一个音高到另一个音高的正弦波滑音

C++ libstd 同时计算 sin 和 cos