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

时间:2015-07-26 19:58:51

标签: 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";
}

输出:

  

isnan(value1):true

     

isnan(value2):false

     

isnan(value3):false

     

value1:3.3614005946481929011e-4764

     

value2:9.7056260598879139386e-4764

     

value3:6.3442254652397210376e-4764

此处value1被归类为&#34;不支持&#34;由387或更高,因为它具有非零而非全部指数 - 它实际上是&#34;非常规&#34;。并且isnan按预期工作:值实际上不是数字(尽管不完全是NaN)。第二个值value2设置了整数位,并且也按预期工作:它不是NaN。第三个是缺失整数位的值。

但不知何故,两个数字value1value2都会打印出来,而且这些值与缺少的整数位完全不同!这是为什么?我尝试的所有其他方法,例如printfto_string只提供0.00000

更奇怪的是,如果我使用value1进行任何算术,在后续打印中我会得到nan。考虑到这一点,operator<<(long double)如何设法实际打印除nan以外的任何内容?它是显式设置整数位,还是解析数字而不是对它进行任何FPU算术? (假设在Linux 32位上使用g ++ 4.8)。

2 个答案:

答案 0 :(得分:1)

  

我尝试过的所有其他方法,比如printf和to_string   0.00000

operator<<(long double)实际执行的操作是使用num_put<>库中的locale类来执行数字格式设置,而后者又使用其中一个printf - 族函数(请参阅C ++标准的第27.7.3.6和22.4.2.2节。

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

在您(和我的)案例中,似乎是%LG

%Lg
  

value1:0.00000000000000000000

     

value1:3.36140059464819290106e-4764

     

value1:0x4.3d1ac8f246f235200000p-15826

     

value1:3.3614005946481929011e-4764

     

value1:3.3614005946481929011e-4764

  

考虑到这一点,运营商&lt;&lt;(long double)如何管理   实际打印除了南?它是否显式设置整数   位,或者它可能会解析数字而不是执行任何FPU算术   在它上面?

打印非标准化值。

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"; 使用的从二进制到十进制浮点表示的转换可以在没有任何FPU算术的情况下执行。您可以在printf()源文件中找到glibc实现。

答案 1 :(得分:0)

我正在尝试这个:

long double value = std::numeric_limits<long double>::quiet_NaN();
std::cout << "isnan(value): " << std::boolalpha << std::isnan(value) << "\n";
std::cout << "value: " << std::setprecision(20) << value << "\n";

所以我的假设就像这里所说的那样:http://en.cppreference.com/w/cpp/numeric/math/isnan当被std::isnanstd::numeric_limits<long double>::quiet_NaN() != std::numeric_limits<double>::quiet_NaN() 评估时,{@ 3}}值被转换为double而不是long double:

{{1}}