The following code assumes that we are on an x86-compatible system and that long double
maps to x87 FPU's 80-bit format.
#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";
}
Output:
isnan(value1): true
isnan(value2): false
isnan(value3): false
value1: 3.3614005946481929011e-4764
value2: 9.7056260598879139386e-4764
value3: 6.3442254652397210376e-4764
Here value1
is classified as "unsupported" by 387 and higher, because it has nonzero and not all-ones exponent — it's in fact an "unnormal". And isnan
works as expected with it: the value is indeed nothing of a number (although not exactly a NaN). The second value, value2
, has that integer bit set, and also works as expected: it's not a NaN. The third one is the value of the missing integer bit.
But somehow both numbers value1
and value2
appear printed, and the values differ exactly by the missing integer bit! Why is that? All other methods I tried, like printf
and to_string
give just 0.00000
.
Even stranger, if I do any arithmetic with value1
, in subsequent prints I do get nan
. 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? (assuming g++4.8 on Linux 32 bit).
NaN stands for “Not a Number”. It is part of the floating point specification. You can get a NaN by performing illegal mathematical operations on a floating point variable, dividing by 0, e.g.
Method 1: Imputation with specific values. In this method NaN values are changed with a specific value (a number for example), in most cases, this is 0. Sometimes it is the best option, like when your feature is the amount of money spent on sweets, but sometimes it is the worst one, like for age.
This obstacle is known as NaN values in Data Science and Machine Learning. What are NaN values? NaN or Not a Number are special values in DataFrame and numpy arrays that represent the missing of value in a cell. In programming languages they are also represented, for example in Python they are represented as None value.
Languages like C or C++ define macros for dealing with NaN, like isNan: Some programmers may use NaN as quick and dirty way to return an error condition from a function returning a floating point number. They should probably throw an exception instead. What is C++ used for?
All other methods I tried, like printf and to_string give just 0.00000.
What operator<<(long double)
actually does is using the num_put<>
class from locale
library to perform the numeric formatting, which in turn uses one of the printf
-family functions (see sections 27.7.3.6 and 22.4.2.2 of the C++ standard).
Depending on the settings, printf conversion specifier used for long double
by locale
might be any of: %Lf
, %Le
, %LE
, %La
, %LA
, %Lg
or %LG
.
In your (and my) case it seems to be %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?
It prints the unnormalized value.
Conversion from binary to decimal floating point representation used by printf()
may be performed without any FPU arithmetics. You can find the glibc implementation in the stdio-common/printf_fp.c
source file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With