Jenny
Jenny

Reputation: 33

How many floating-point types are there in C?

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

Answers (3)

Steve Summit
Steve Summit

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

phuclv
phuclv

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 types float, double, and long 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

0___________
0___________

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

Related Questions