Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do printf and isnan disagree whether a long double value is a NaN?

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)?

like image 360
vitaut Avatar asked Feb 20 '21 19:02

vitaut


People also ask

Why am I getting NaN in c++?

NaN, an acronym for Not a Number is an exception that usually occurs in the cases when an expression results in a number that is undefined or can't be represented. It is used for floating-point operations. For example: The square root of negative numbers.

Why output is NaN?

In JavaScript, NaN stands for Not a Number. It represents a value which is not a valid number. It can be used to check whether a number entered is a valid number or not a number.


1 Answers

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.

Footnote

1 So-called because the significand is commonly represented as b.bbbbbb, 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.

like image 158
Eric Postpischil Avatar answered Sep 27 '22 23:09

Eric Postpischil