C++ asin使用Clang在不同的平台上生成不同的答案

C++ asin使用Clang在不同的平台上生成不同的答案,c++,macos,compiler-construction,clang,C++,Macos,Compiler Construction,Clang,asin(-1)的数学精确值将是-pi/2,这当然是不合理的,不可能精确地表示为浮点值。pi/2的二进制数字以 Apple LLVM version 10.0.0 (clang-1000.11.45.5) Target: x86_64-apple-darwin18.0.0 Thread model: posix InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/

asin(-1)
的数学精确值将是
-pi/2
,这当然是不合理的,不可能精确地表示为
浮点值。
pi/2
的二进制数字以

Apple LLVM version 10.0.0 (clang-1000.11.45.5)
Target: x86_64-apple-darwin18.0.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
您的前三个库(正确地)将此循环到

在MacOS上,它似乎被截断为:

1.10010010000111111011011_2 = 1.57079637050628662109375_10
这是一个小于1 ULP的错误(最后一位的单位)。这可能是由不同的实现引起的,或者FPU设置为不同的舍入模式,或者在某些情况下,编译器在编译时计算值

我认为C++标准并没有保证超越函数的正确性。
如果您的代码确实依赖于(平台/硬件独立的)准确性,我建议使用库,例如。否则,你就要接受不同。或者看一看代码< ASIN < /Cuff>函数的来源,每种情况下都调用它。

< P> <代码> ASI< /COD>在 LBM中实现,这是标准C库的一部分,而不是C++标准库。(技术上,C++标准库包括C库函数,但实际上GNU和LLVM C++库实现依赖于底层平台数学库。)三个平台——Linux、OS X和Windows——每个都有自己的数学库的实现,所以如果正在使用库函数,它当然可能是一个不同的库函数,结果可能在最后一位位置不同(这是您的测试所显示的)

但是,很可能在所有情况下都不会调用库函数。这将取决于编译器和您传递给它们的优化选项(可能还有其他一些选项)。由于
asin
函数是标准库的一部分,因此具有已知的行为,因此编译器在编译时计算
std::asin(-1.0F)
的值是完全合法的,就像对任何其他常量表达式一样(如
1.0+1.0
,几乎任何编译器在编译时都会不断折叠到
2.0

由于您没有提到您正在使用的优化设置,因此很难确切地说出发生了什么,但我做了一些测试以获得基本的想法:

  • GCC常量将对
    asin
    的调用折叠起来,不带任何优化标志,但它不会在
    printf
    中预计算参数提升(它将
    a
    转换为
    double
    ,以便将其传递给
    printf
    ),除非您至少指定了
    -O1
    (使用GCC 8.3测试)

  • Clang(7.0)调用标准库函数,除非您至少指定了
    -O2
    。但是,如果您显式调用
    asinf
    ,它将在
    -O1
    处常量折叠。如图所示

  • MSVC(v19.16)没有固定的折叠。它要么调用
    std::asin
    包装器,要么直接调用
    asinf
    ,具体取决于优化设置。我不太了解包装器的功能,也没有花太多时间研究

GCC和Clang常量都将表达式折叠为完全相同的二进制值(0xBFF921FB60000000作为双精度值),即二进制值-1.1001001000011111011(尾部的零被截断)

请注意,三个平台上的
printf
实现之间也存在差异(
printf
也是平台C库的一部分).理论上,您可以从相同的二进制值中看到不同的十进制输出,但由于在调用
printf
之前,
printf
的参数被提升为
double
,并且提升被精确定义,且不改变值,因此在这种特殊情况下,这不太可能产生任何影响


作为补充说明,如果您真的关心第七个小数点,请使用
double
而不是
float
。实际上,您应该只在精度不重要的非常特定的应用程序中使用
float
;正常的浮点类型是
double
,前7位看起来完全相同。这就足够了吗ent?曾经有一个类似的问题/答:。差异可能来自将值转换为文本的代码,也可能来自结果的低位。无论哪种方式,由于
float
支持6位数字,这些差异都深深地隐藏在噪声中。(正式地说,
float
需要至少支持六位数字;典型的实现只支持六位数字)问题是什么?为什么会有差异?或者这是一个错误?这可能取决于它是在库中计算的还是指向CPU命令。我曾经读过一篇关于Intel CPU上的H/W sqrt与s/W sqrt()的令人不安的故事。
1.1001001000011111101101010100010001000010110100011000010001101..._2
1.10010010000111111011011_2 = 1.57079637050628662109375_10
1.10010010000111111011010_2 = 1.57079625129699707031250_10