vit*_*aut 5 c c++ floating-point clang
考虑以下代码:
#include <math.h>
#include <stdio.h>
#include <string.h>
int main() {
__uint128_t n = (__uint128_t(0x00007ffff7dd6e65ULL) << 64) |
0x63696c400a2d2d21ULL;
long double d = 0;
memcpy(&d, &n, sizeof(long double));
printf("%d\n", isnan(d));
printf("%Le\n", d);
}
Run Code Online (Sandbox Code Playgroud)
当使用 clang 版本 12.0.0 (clang-1200.0.32.29) 编译并在 macOS 上运行时,它会产生以下输出:
1
3.345927e+3575
Run Code Online (Sandbox Code Playgroud)
为什么将其isnan报告long double为 NaN 而将其printf打印为3.345927e+3575?
iostreams 和 clang++ 也是如此:
std::cout << d; // prints 3.34593e+3575
Run Code Online (Sandbox Code Playgroud)
具体来说,为什么在处理这个数字时不同的 C 和 C++ API 之间的行为存在差异(这似乎是不正常的扩展精度数字)?
由 128 位整数初始化形成的对象无效,因为其显式有效位与其他位不匹配。
Apple Clang 使用的是 Intel 的 80 位浮点格式。根据英特尔 64 位和 IA-32 架构软件开发人员手册(2017 年 12 月)4.2.2,“整数”位明确设置为 1 表示无穷大、正常数和 NaN,设置为 0 表示次正规数和零。整数1位是有效数的前导位,编码中的位 63。
有效数的 64 位位于 128 位整数的低位,问题中的代码将它们设置为 63696c400A2D2D21 16。其中,第 63 位为 0(高位 6 为 0110 2)。由于指数字段是 6E65 16(在第 79 到 64 位),这应该是一个正常数,所以第 63 位应该是 1。
当 Integer 位设置不正确时,我没有看到行为规范,因此我们可能认为它没有定义。(人们可能想知道这是否是 的故意行为isnan,因为它“正确”地报告了无效编码不是数字。)
当0x6369…更正为 时0xE369…,程序会正确报告该值不是 NaN。
1之所以如此称呼是因为有效数通常表示为b。bbb ... bbb,其中前导位是小数点的唯一左侧,因此是唯一表示整数值的位。有效数的其余位是小数位。