2

printf("%Lf", long double) truncates the output to double. Or, am I doing something wrong?

Gcc 9.3.0 Ubuntu 20.04.2, Linux 5.8.0-59-generic

printf("print m_pi  %.45f\n", M_PI);
printf("print m_pil %.45LF\n", M_PIl);
printf("maple Pi    3.141592653589793238462643383279502884197\n");
printf("m_pil def   3.141592653589793238462643383279502884L\n");

Output:

print m_pi  3.141592653589793115997963468544185161590576172
print m_pil 3.141592653589793238512808959406186204432742670
maple Pi    3.141592653589793238462643383279502884197
m_pil def   3.141592653589793238462643383279502884L

It seems like gcc printf is truncating long double to 13 digits +-.

phuclv
  • 37,963
  • 15
  • 156
  • 475

1 Answers1

2

printf("%Lf", long double) truncates the output to double

It clearly doesn't truncate anything. It's easy to see the result for double and long double are completely different and long double are more precise than double

So there's nothing wrong with it. On x86 long double is typically 80-bit extended precision type whose precision is around 18 digits, and 3.141592653589793238 is correct to 19 significant digits

If you need some more precision then use quadruple precision by setting the -mlong-double-128 option or use __float128 directly. Of course this is emulated by software so it'll be significantly slower than the native x87 80-bit format

Demo on Godbolt

For higher precisions you'll have to use a 3rd party arbitrary-precision floating-point library


Why is size(long double) 16 bytes?

Size has nothing to do with precision. Just like trap representations and padding bits are allowed in C++, a type's size can be wider than the real precision

In the 80-bit extended format the internal representation is 10 bytes but padding bytes are added to align the type to 4 bytes (x86) or 8 bytes (x86-64), resulting in a 16-byte type in 64-bit mode. The padding can be changed by the -m96/128bit-long-double options in gcc

What's important here is LDBL_DIG/LDBL_MANT_DIG/LDBL_MAX_EXP/LDBL_MAX_10_EXP or std::numeric_limits<long double>::digits/digits10/max_exponent/max_exponent10which are the real precision of the type. In fact by changing the combinations of the above -m-long-double options you'll get many different results:

The default result is size = 16, digits10 = 18, digits2 = 64. You'll get size = 10 if you disable padding, but that'll come at a performance expense due to misalignment. See more demo on Godbolt

See

In PowerPC you can also see the same phenomena when changing the floating-point format. While the size is still 16 bytes, the precision can vary. With -mabi=ibmlongdouble (double-double arithmetic, which is the default) you'll get (size, digits10, digits2) = (16, 31, 106) but with -mabi=ieeelongdouble the tuple will become (16, 33, 113)

phuclv
  • 37,963
  • 15
  • 156
  • 475