Reputation: 55524
Consider the following code:
#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);
}
When compiled with clang version 12.0.0 (clang-1200.0.32.29) and run on macOS it produces the following output:
1
3.345927e+3575
Why does isnan
reports this long double
as NaN while printf
prints it as 3.345927e+3575
?
Same happens with iostreams and clang++:
std::cout << d; // prints 3.34593e+3575
Specifically, why is there is a difference in behavior between different C and C++ APIs when handling this number (which appears to be unnormal extended precision number)?
Upvotes: 6
Views: 363
Reputation: 222352
The object formed by the initialization from the 128-bit integer is invalid because its explicit significand bit does not match the other bits.
Apple Clang is using Intel’s 80-bit floating-point format. According to Intel 64 and IA-32 Architectures Software Developer’s Manual (December 2017) 4.2.2, the “Integer” bit is explicitly set to 1 for infinities, normal numbers, and NaNs and to 0 for subnormals and zeros. The Integer1 bit is the leading bit of the significand, bit 63 in the encoding.
The 64 bits of the significand are in the lower bits of the 128-bit integer, and the code in the question sets them to 63696c400A2D2D2116. In this, bit 63 is 0 (the high digit, 6, is 01102). Since the exponent field is 6E6516 (in bits 79 to 64), this should be a normal number, so bit 63 should be 1.
I do not see a specification of the behavior when the Integer bit is improperly set, so we may expect it is not defined. (One might wonder whether this is intentional behavior of isnan
, as it is “correctly” reporting that an invalid encoding is not a number.)
When 0x6369…
is corrected to 0xE369…
, the program correctly reports the value is not a NaN.
1 So-called because the significand is commonly represented as b.bbb…bbb, where the leading bit is the only one left of the radix point and hence is the only bit representing an integer value. The remaining bits of the significand are fraction bits.
Upvotes: 8