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));
Answer from phuclv on Stack Overflow
🌐
Yale University
cs.yale.edu › homes › aspnes › pinewiki › C(2f)FloatingPoint.html
C/FloatingPoint
Real numbers are represented in C by the floating point types float, double, and long double. Just as the integer types can't represent all integers because they fit in a bounded number of bytes, so also the floating-point types can't represent all real numbers.
🌐
GeeksforGeeks
geeksforgeeks.org › c language › c-float-and-double
C Float and Double - GeeksforGeeks
June 12, 2026 - Float and Double are primitive data types in C used to store decimal (floating-point) values.
🌐
Substack
alexanderobregon.substack.com › alexander obregon's substack › floating point math in c for beginners
Floating Point Math in C for Beginners
February 14, 2026 - In C, floating point values rely on a small set of types and a specific binary layout that most compilers follow. Values that you write as decimal literals in source code end up as binary fractions in memory, and that mapping is what gives float ...
🌐
Learn C
learnc.net › home › learn c programming › c float
C Float Types
April 13, 2025 - C provides various floating-point types to represent non-integer number with a decimal point at any position.
🌐
GNU
gnu.org › software › c-intro-and-ref › manual › html_node › Floating_002dPoint-Data-Types.html
Floating-Point Data Types (GNU C Language Manual)
Next: Complex Data Types, Previous: Integer Data Types, Up: Primitive Data Types [Contents][Index] Floating point is the binary analogue of scientific notation: internally it represents a number as a fraction and a binary exponent; the value is that fraction multiplied by the specified power of 2.
Top answer
1 of 3
9

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));
2 of 3
3

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.

🌐
W3Schools
w3schools.com › c › c_data_types_numbers.php
C Numbers
C number types: int for whole numbers, float and double for values with decimals.
Find elsewhere
🌐
TutorialsPoint
tutorialspoint.com › float-and-double-in-c
Float and Double in C
September 2, 2023 - In C programming, float and double are data types used to represent floating point numbers.
🌐
Cornell Computer Science
cs.cornell.edu › courses › cs3410 › 2024fa › notes › float.html
Floating Point - CS 3410
The float type gets its name because, unlike a fixed-point representation, it lets the binary point float around. It does that by putting the point position right into the value itself. This way, every float can have a different \(e\) value, so different floats can exist on very different scales:
🌐
W3Schools
w3schools.com › c › ref_keyword_float.php
C float Keyword
❮ Previous C Keyword Reference Next ❯ · Print a floating-point number: float myNum = 5.75; printf("%f", myNum); Try it Yourself » · Share Link Copied · The float keyword is a data type which stores fractional numbers.
🌐
GNU
gnu.org › software › c-intro-and-ref › manual › html_node › Floating-Constants.html
Floating Constants (GNU C Language Manual)
A floating-point constant normally has type double. You can force it to type float by adding ‘f’ or ‘F’ at the end.
🌐
LabEx
labex.io › tutorials › c-work-with-floating-point-arithmetic-in-c-435202
How to Work with Floating-Point Arithmetic in C | LabEx
In this lab, you will learn how to work with floating-point arithmetic in C. You will start by declaring float and double variables, then perform various arithmetic operations on them, and finally, print the results with appropriate precision.
🌐
Cornell Computer Science
cs.cornell.edu › courses › cs3410 › 2026sp › notes › float.html
Floating Point - CS 3410 Spring 2026
The float type gets its name because, unlike a fixed-point representation, it lets the binary point float around. It does that by putting the point position right into the value itself. This way, every float can have a different \(e\) value, so different floats can exist on very different scales:
🌐
GNU
gnu.org › software › c-intro-and-ref › manual › html_node › Floating-Representations.html
Floating Representations (GNU C Language Manual)
GNU C uses the floating-point representations specified by the IEEE 754-2008 Standard for Floating-Point Arithmetic.
🌐
ZetCode
zetcode.com › clang › float-type
C Floating-Point Types - Complete Guide
April 1, 2025 - The float.h header defines constants like FLT_MANT_DIG that reveal implementation details. Note that long double precision varies by architecture. Floating-point numbers use sign-exponent-mantissa format:
🌐
Learn C++
learncpp.com › cpp-tutorial › floating-point-numbers
4.8 — Floating point numbers – Learn C++
If you’re from a country that uses a decimal comma, you’ll need to get used to using a decimal point instead. ... C++ has three fundamental floating point data types: a single-precision float, a double-precision double, and an extended-precision long double.
🌐
TechOnTheNet
techonthenet.com › c_language › variables › create_float.php
C Language: Float Variables
You can define a variable as a float and assign a value to it in a single declaration.