As many programmers know there are several floating-point types in C. So far I know float, double and long double but I'm not quite sure they are all of them, cause I found several definitions like __DEC32_MAX__. At first I thought that is another name for __FLT_MAX__ but when I tried to print it, I realized that it's different (as in below):
#include <stdio.h>
void main(void)
{
__mingw_printf("flt value: %e, size: %d\n", __FLT_MAX__, sizeof(__FLT_MAX__));
__mingw_printf("dbl value: %e, size: %d\n", __DBL_MAX__, sizeof(__DBL_MAX__));
__mingw_printf("ldbl value: %e, size: %d\n", __LDBL_MAX__, sizeof(__LDBL_MAX__));
__mingw_printf("dec32 value: %e, size: %d\n", __DEC32_MAX__, sizeof(__DEC32_MAX__));
__mingw_printf("dec64 value: %e, size: %d\n", __DEC64_MAX__, sizeof(__DEC64_MAX__));
__mingw_printf("dec128 value: %e, size: %d\n", __DEC128_MAX__, sizeof(__DEC128_MAX__));
}
/* output:
flt value: 3.402823e+038, size: 4
dbl value: 1.797693e+308, size: 8
ldbl value: 3.237664e-317, size: 16
dec32 value: 9.944455e-315, size: 4
dec64 value: 9.089022e+269, size: 8
dec128 value: 3.237656e-317, size: 16
*/
What are __DEC__* s?
Are there any other floating-point types that I don't know?
There are only 3 real floating-point types in C before C23: float, double and long double, which can use arbitrary base for the significand like decimal or octal although nowadays it's most likely binary. Some older systems use hexadecimal floating-point types. Each of the real floating types can be combined with the _Complex or _Imaginary keyword to make complex floating types
C23 added 3 more real floating-point types: _Decimal32, _Decimal64, and _Decimal128, all are decimal types
Anything else if you see would be compiler extensions or internal compiler things. Those would be prefixed with __ because identifiers beginning with double underscores are reserved for implementations.
__mingw_printf, __DBL_MAX__, __DEC64_MAX__... are all internal to the implementation and you aren't supposed to use them directly. Use FLT_MAX instead of __FLT_MAX__, DBL_MAX instead of __DBL_MAX__, etc. Similarly avoid calling __mingw_printf(), just define __USE_MINGW_ANSI_STDIO and let everything automatically handled when calling printf(). See also Difference between INT_MAX and __INT_MAX__ in C
In fact before C23 there's a decimal floating extension in GCC
As an extension, GNU C supports decimal floating types as defined in the N1312 draft of ISO/IEC WDTR24732. Support for decimal floating types in GCC will evolve as the draft technical report changes. Calling conventions for any target might also change. Not all targets support decimal floating types.
The decimal floating types are
_Decimal32,_Decimal64, and_Decimal128. They use a radix of ten, unlike the floating typesfloat,double, andlong doublewhose radix is not specified by the C standard but is usually two.6.14 Decimal Floating Types
Lots of new features in C are from previous extensions in POSIX or GCC. The names _Decimal* were chosen because identifiers beginning with _ following by an uppercase character are also reserved. See What are the rules about using an underscore in a C identifier?. That's why most new C standard types or keywords begin with _Uppercase to reduce the chance of name clashing, due to the lack of proper namespace handling in C
Since mingw is a GCC port, it also supports that decimal floating extension. In float.h the standard constants would be defined as the implementation-specific constants, for example
#define LDBL_MAX __LDBL_MAX__
#define DEC128_MAX __DEC128_MAX__
Your code also invokes undefined behavior because you use the wrong format specifier. Firstly, sizeof returns a size_t value which must be printed using %zu, so all your __mingw_printf calls are wrong. Secondly, %e accepts a double, so using it for __LDBL_MAX__, __DEC32_MAX__, __DEC64_MAX__, __DEC128_MAX__ is even worse
So how to print those _Decimal* types? As mentioned in the GCC Decimal Floating Types extension documentation above
GCC does not provide the C library functionality associated with math.h, fenv.h, stdio.h, stdlib.h, and wchar.h, which must come from a separate C library implementation. Because of this the GNU C compiler does not define macro
__STDC_DEC_FP__to indicate that the implementation conforms to the technical report.
That means you must do your own IO. One solution is to use IBM libdfp which adds a printf implementation with H/D/DD length modifiers for _Decimal32/64/128 respectively. C23 adds that same set for printing those new types. So your code should be like this
printf("flt value: %e, size: %zu\n", FLT_MAX, sizeof(FLT_MAX));
printf("dbl value: %le, size: %zu\n", DBL_MAX, sizeof(DBL_MAX));
printf("ldbl value: %Le, size: %zu\n", LDBL_MAX, sizeof(LDBL_MAX));
printf("dec32 value: %He, size: %zu\n", DEC32_MAX, sizeof(DEC32_MAX));
printf("dec64 value: %De, size: %zu\n", DEC64_MAX, sizeof(DEC64_MAX));
printf("dec128 value: %DDe, size: %zu\n", DEC128_MAX, sizeof(DEC128_MAX));
The decimal types are very special, very interesting, and very much not in widespread use. They use an internal fraction representation which is based on base 10, not base 2. See in particular IEEE 754-2008.
So all of the important, unfortunate, tiresome arguments about 0.1 not being exactly representable, about 0.1 + 0.2 not exactly equaling 0.3, go away, and almost miraculously computer floating point can behave just like the decimal floating point we're all used to! But these types, and in particular the printf support they'll require, are not quite mainstream — yet.
In IEEE 754-2008, the two principal decimal types are decimal32 and decimal64, which are the decimal counterparts to our familiar binary float and double. There is also decimal128 analogous to long double.
Footnote: Strictly speaking, it's not quite correct to say that decimal32 and the rest are the "decimal counterparts" to our "familiar binary" floating-point types like float and double. The new decimal types are, AFAIK, indeed guaranteed to be decimal. But the traditional types float, double, and long double were not guaranteed to be binary. They usually were, on virtually every machine you're ever likely to use, but they could also be decimal, on one of the rare old machines that implemented floating-point natively and only in decimal. On such a machine, you'd notice that FLT_RADIX from <float.h> was 10.
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