asin使用Clang在

时间:2019-03-19 16:02:48

标签: c++ macos compiler-construction clang

#include <cmath>
#include <cstdio>

int main() {
    float a = std::asin(-1.f);
    printf("%.10f\n", a);
    return 0;
}

我使用clang,g ++和Visual Studio在多个平台上运行了以上代码。他们都给了我相同的答案:-1.5707963705

如果我使用clang在macOS上运行它,它将得到-1.5707962513。 macOS上的Clang应该使用libc ++,但是macOS是否有自己的libc ++实现?

如果我运行clang --verison,我会得到:

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

2 个答案:

答案 0 :(得分:4)

asinlibm中实现,它是标准C库的一部分,而不是C ++标准库的一部分。 (从技术上讲,C ++标准库包括C库函数,但实际上Gnu和LLVM C ++库实现都依赖于基础平台数学库。)这三个平台(Linux,OS X和Windows)各自具有自己的平台。数学库的实现,因此,如果使用库函数,则它肯定是一个不同的库函数,并且结果的最后一位位置(测试显示的结果)可能会有所不同。

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

由于您没有提及正在使用的优化设置,因此很难确切说明正在进行的操作,但是我对http://gcc.godbolt.org做了一些测试,以了解基本概念:

  • GCC 常量将调用折叠到asin,而没有任何优化标志,但不会在printf中预先计算参数提升(这会转换{{1} }传递给a,以将其传递给double),除非您至少指定了printf。 (已通过GCC 8.3测试)。

  • 除非您至少指定-O1,否则
  • Clang (7.0)会调用标准库函数。但是,如果您显式调用-O2,它的常数将折叠为asinf。走吧。

  • MSVC (v19.16)并非恒定折叠。根据优化设置,它要么调用-O1包装器,要么直接调用std::asin。我不太了解包装程序的功能,也没有花太多时间进行调查。

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

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

请注意,如果您确实关心第七位小数点,请使用printf而不是double。实际上,您只应在精度不重要的非常特定的应用程序中使用float。正常的浮点类型为float

答案 1 :(得分:3)

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

开头
1.1001001000011111101101010100010001000010110100011000010001101..._2

您的前三个库将此(正确)舍入为

1.10010010000111111011011_2 = 1.57079637050628662109375_10

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

1.10010010000111111011010_2 = 1.57079625129699707031250_10

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

我认为C ++标准并不能真正保证先验功能的准确性。 如果您的代码确实取决于(与平台/硬件无关)准确性,那么我建议您使用MPFR之类的库。否则,就是与众不同。或者看看每种情况下调用的asin函数的来源。