虽然是NaN,为什么打印这个值?

Rus*_*lan 11 c++ floating-point nan

以下代码假定我们在x86兼容系统上并long double映射到x87 FPU的80位格式.

#include <cmath>
#include <array>
#include <cstring>
#include <iomanip>
#include <iostream>

int main()
{
    std::array<uint8_t,10> data1{0x52,0x23,0x6f,0x24,0x8f,0xac,0xd1,0x43,0x30,0x02};
    std::array<uint8_t,10> data2{0x52,0x23,0x6f,0x24,0x8f,0xac,0xd1,0xc3,0x30,0x02};
    std::array<uint8_t,10> data3{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x30,0x02};
    long double value1, value2, value3;
    static_assert(sizeof value1 >= 10,"Expected float80");
    std::memcpy(&value1, data1.data(),sizeof value1);
    std::memcpy(&value2, data2.data(),sizeof value2);
    std::memcpy(&value3, data3.data(),sizeof value3);
    std::cout << "isnan(value1): " << std::boolalpha << std::isnan(value1) << "\n";
    std::cout << "isnan(value2): " << std::boolalpha << std::isnan(value2) << "\n";
    std::cout << "isnan(value3): " << std::boolalpha << std::isnan(value3) << "\n";
    std::cout << "value1: " << std::setprecision(20) << value1 << "\n";
    std::cout << "value2: " << std::setprecision(20) << value2 << "\n";
    std::cout << "value3: " << std::setprecision(20) << value3 << "\n";
}
Run Code Online (Sandbox Code Playgroud)

输出:

isnan(value1):true

isnan(value2):false

isnan(value3):false

value1:3.3614005946481929011e-4764

value2:9.7056260598879139386e-4764

value3:6.3442254652397210376e-4764

这里value1被387及更高版本归类为"不支持",因为它具有非零而非全部指数 - 它实际上是"不正常".并且isnan按预期工作:价值确实不是一个数字(尽管不完全是NaN).第二个值value2设置了整数位,并且也按预期工作:它不是NaN.第三个是缺失整数位的值.

但不知何故,这两个数字value1,并value2出现印刷,值由缺少整数位不同正是!这是为什么?我试过的所有其他方法,喜欢printfto_string给予0.00000.

更奇怪的是,如果我做任何算术value1,在随后的打印中我确实得到了nan.考虑到这一点,operator<<(long double)甚至如何实际打印任何东西,但nan?它是显式设置整数位,还是解析数字而不是对它进行任何FPU算术?(假设在Linux 32位上使用g ++ 4.8).

Tom*_*zny 1

我尝试过的所有其他方法(例如 printf 和 to_string)仅给出 0.00000。

实际上所做operator<<(long double)的是使用库num_put<>中的类locale来执行数字格式化,这又使用printf-family 函数之一(请参阅 C++ 标准的第 27.7.3.6 和 22.4.2.2 节)。

根据设置,long doubleby使用的printf 转换说明符locale可能是以下任意一种:%Lf%Le%LE%La%LA或。%Lg%LG

在你(和我)的情况下,它似乎是%Lg

printf("value1: %.20Lf\n", value1);
printf("value1: %.20Le\n", value1);
printf("value1: %.20La\n", value1);
printf("value1: %.20Lg\n", value1);
std::cout << "value1: " << std::setprecision(20) << value1 << "\n";
Run Code Online (Sandbox Code Playgroud)

值1:0.00000000000000000000

值1:3.36140059464819290106e-4764

值1:0x4.3d1ac8f246f235200000p-15826

值1:3.3614005946481929011e-4764

值1:3.3614005946481929011e-4764


考虑到这一点,operator<<(long double) 甚至如何设法实际打印除 nan 之外的任何内容?它是否显式设置整数位,或者它可能解析该数字而不是对其进行任何 FPU 算术?

它打印非标准化值。

从二进制到十进制浮点表示的转换printf()可以在没有任何 FPU 算术的情况下执行。您可以在源文件中找到 glibc 实现stdio-common/printf_fp.c