为什么打印这个值虽然是 NaN?
Why is this value printed although being 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
被 387 及更高分类为“不受支持”,因为它具有非零指数且不是全一指数 — 它实际上是“不正常”的。并且 isnan
按预期工作:该值确实不是数字(尽管不完全是 NaN)。第二个值 value2
设置了整数位,并且也按预期工作:它不是 NaN。第三个是缺失整数位的值。
但不知何故,数字 value1
和 value2
都打印出来了,并且值因缺少整数位而完全不同!这是为什么?我尝试过的所有其他方法,如 printf
和 to_string
只给出 0.00000
.
更奇怪的是,如果我用 value1
做任何算术运算,在随后的打印中我会得到 nan
。考虑到这一点,operator<<(long double)
是如何设法实际打印除 nan
之外的任何内容的?它是否显式设置整数位,或者它可能解析数字而不是对其进行任何 FPU 运算? (假设 Linux 32 位上的 g++4.8)。
All other methods I tried, like printf and to_string give just
0.00000.
operator<<(long double)
实际做的是使用 locale
库中的 num_put<>
class 来执行数字格式化,而后者又使用 printf
-系列函数(参见 C++ 标准的第 27.7.3.6 和 22.4.2.2 节)。
根据设置,locale
用于 long double
的 printf 转换说明符可能是以下任何一个:%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";
value1: 0.00000000000000000000
value1: 3.36140059464819290106e-4764
value1: 0x4.3d1ac8f246f235200000p-15826
value1: 3.3614005946481929011e-4764
value1: 3.3614005946481929011e-4764
Taking this into account, how does operator<<(long double) even manage
to actually print anything but nan? Does it explicitly set the integer
bit, or maybe it parses the number instead of doing any FPU arithmetic
on it?
它打印非标准化值。
printf()
使用的从二进制到十进制浮点表示的转换可以在没有任何 FPU 算法的情况下执行。您可以在 stdio-common/printf_fp.c
源文件中找到 glibc 实现。
我正在尝试这个:
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::isnan
评估时被强制转换为 double 而不是 long double 并且严格:
std::numeric_limits<long double>::quiet_NaN() != std::numeric_limits<double>::quiet_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
被 387 及更高分类为“不受支持”,因为它具有非零指数且不是全一指数 — 它实际上是“不正常”的。并且 isnan
按预期工作:该值确实不是数字(尽管不完全是 NaN)。第二个值 value2
设置了整数位,并且也按预期工作:它不是 NaN。第三个是缺失整数位的值。
但不知何故,数字 value1
和 value2
都打印出来了,并且值因缺少整数位而完全不同!这是为什么?我尝试过的所有其他方法,如 printf
和 to_string
只给出 0.00000
.
更奇怪的是,如果我用 value1
做任何算术运算,在随后的打印中我会得到 nan
。考虑到这一点,operator<<(long double)
是如何设法实际打印除 nan
之外的任何内容的?它是否显式设置整数位,或者它可能解析数字而不是对其进行任何 FPU 运算? (假设 Linux 32 位上的 g++4.8)。
All other methods I tried, like printf and to_string give just 0.00000.
operator<<(long double)
实际做的是使用 locale
库中的 num_put<>
class 来执行数字格式化,而后者又使用 printf
-系列函数(参见 C++ 标准的第 27.7.3.6 和 22.4.2.2 节)。
根据设置,locale
用于 long double
的 printf 转换说明符可能是以下任何一个:%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";
value1: 0.00000000000000000000
value1: 3.36140059464819290106e-4764
value1: 0x4.3d1ac8f246f235200000p-15826
value1: 3.3614005946481929011e-4764
value1: 3.3614005946481929011e-4764
Taking this into account, how does operator<<(long double) even manage to actually print anything but nan? Does it explicitly set the integer bit, or maybe it parses the number instead of doing any FPU arithmetic on it?
它打印非标准化值。
printf()
使用的从二进制到十进制浮点表示的转换可以在没有任何 FPU 算法的情况下执行。您可以在 stdio-common/printf_fp.c
源文件中找到 glibc 实现。
我正在尝试这个:
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::isnan
评估时被强制转换为 double 而不是 long double 并且严格:
std::numeric_limits<long double>::quiet_NaN() != std::numeric_limits<double>::quiet_NaN()