Reputation: 33
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?
Upvotes: 3
Views: 954
Reputation: 48020
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.
Upvotes: 3
Reputation: 41952
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 double
whose 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));
Upvotes: 9
Reputation: 67820
Some specific uses require small floats like: float8
and float16
and bifloat
It is used mainly in the DSP, AI and graphics applications. Many hardware architectures support at least some of them (for example bifloat AVX-512 BF16, ARM-V8.8, AMD ROCm, CUDA and some more).
There are also big float formats like float128. Example hardware: VSX in PowerPC.
Those floats are not supported directly by the standard C. Some compilers support it as an extension.
Upvotes: 2