3
\$\begingroup\$

I have created my own personal data structure libraries for C and I've re-engineered it about two times now and I've added alot of preprocessor checks and defines concerning OS type and compiler type.

One thing I've wanted to add to my personal libraries is having an informal "standard" fixed-size floating-point types.

#ifndef __float32_t_defined
#   if defined(COMPILER_CLANG) || defined(COMPILER_GCC)
#       define __float32_t_defined
        typedef float float32_t TYPE_MODE(__SF__);
#   elif FLT_MANT_DIG==24
#       define __float32_t_defined
        typedef float float32_t;
#   elif DBL_MANT_DIG==24
#       define __float32_t_defined
        typedef double float32_t;
#   endif
#endif

#ifndef __float64_t_defined
#   if defined(COMPILER_CLANG) || defined(COMPILER_GCC)
#       define __float64_t_defined
        typedef float float64_t TYPE_MODE(__DF__);
#   elif FLT_MANT_DIG==53
#       define __float64_t_defined
        typedef float float64_t;
#   elif DBL_MANT_DIG==53
#       define __float64_t_defined
        typedef double float64_t;
#   elif LDBL_MANT_DIG==53
#       define __float64_t_defined
        typedef long double float64_t;
#   endif
#endif

#ifndef __floatmax_t_defined
#   if defined(COMPILER_CLANG) || defined(COMPILER_GCC)
#       define __floatmax_t_defined
        typedef double floatmax_t TYPE_MODE(__XF__);
#   elif LDBL_MANT_DIG>=64
#       define __floatmax_t_defined
        typedef long double floatmax_t;
#   elif DBL_MANT_DIG>=64
#       define __floatmax_t_defined
        typedef double floatmax_t;
#   else
#       define __floatmax_t_defined
        typedef float floatmax_t;
#   endif
#endif

For Clang and GCC, I use the compiler extension that checks machine mode and sets the type definition to the largest width of the floating point type. I added further type definition checks in the event Clang or GCC are not used.

my main question is that, given the macros in float.h, is checking float size by its mantissa a wise/safe thing to do? If not, what alternative can I take?

\$\endgroup\$
  • \$\begingroup\$ It the goal to have a 32-bit float32_t, a 64-bit float64_t regardless of their internal proprieties? \$\endgroup\$ – chux Jul 25 at 2:00
  • \$\begingroup\$ What is the goal of floatmax_t? \$\endgroup\$ – chux Jul 25 at 2:05
  • \$\begingroup\$ @chux No, the goal of float32_t and float64_t is that those types are always their fixed size. The goal of floatmax_t is to be the largest float width possible, which might either be the same as float32_t, or float64_t, or even higher than those two. \$\endgroup\$ – Nergal Jul 26 at 18:06
1
\$\begingroup\$

float32_t float64_t

Since the goal is:

float32_t and float64_t is that those types are always their fixed size

the macros nearly achieve that.


is checking float size by its mantissa a wise/safe thing to do? If not, what alternative can I take?

Big caveat: OP's expressed goal is same size, not same encoding. Two systems may have a 32-bit float, yet different mantissa, exponent range and other properties. The biggest "safety" concern is assuming same floating point properties with same size.

C allows for a great diversity of implementations though. The macros accommodate most current compilers well, yet they are not specified to be always typedef 32/64 bit types.

To be clear, checking xxx_MANT_DIG, FLT_RADIX, xxx_MIN_EXP, ... is not sufficient, in general, even though it may be practically sufficient.

It fundamental comes down to:

what should code do if it encounters a novel implementation?

I'd go with testing for the usual suspects and expand code when I encounter a true counter case.

#ifndef __float64_t_defined
  #if FLT_RADIX==2 && DBL_MANT_DIG==53 && DBL_MIN_EXP == -1021
    #define __float64_t_defined
    typedef double float64_t;
  #elif FLT_RADIX==2 && FLT_MANT_DIG==53 && FLT_MIN_EXP == -1021
    #define __float64_t_defined
    typedef float float64_t;
  // Expand list for variants that I have found 
  #elif FLT_RADIX==2 && DBL_MANT_DIG==TBD && DBL_MIN_EXP == TBD
  #else
    #error TBD code for float64_t
  #endif
#endif

Note: Code like DBL_MANT_DIG==24 is not even C compliant with FLT_RADIX==2. DBL_MANT_DIG >= 34 to meet precision requirements. This looks like a test for a non-real machine.


I recommend 2 additions:

Report fail to typedef right away with #error

#   elif LDBL_MANT_DIG==53
#       define __float64_t_defined
        typedef long double float64_t;

// Add
#   else
#     error TBD code for float64_t

#   endif

With C11 or later, use _Static_assert

_Static_assert(sizeof (float64_t)*CHAR_BIT == 64, "Unexpected `float64_t` size");

With early compilers, see Static assert in C and others.


floatmax_t

After taking advantage or known COMPILER_CLANG, COMPILER_GCC compilers, I'd recommend more conventional code - Simply test for C99 or later.

#ifndef __floatmax_t_defined
#   if defined(COMPILER_CLANG) || defined(COMPILER_GCC)
#       define __floatmax_t_defined
        typedef double floatmax_t TYPE_MODE(__XF__);
#   elif (__STDC_VERSION__ >= 199901L)
#       define __floatmax_t_defined
        typedef long double floatmax_t;
#   else
#       define __floatmax_t_defined
        typedef double floatmax_t;
#   endif
#endif

Similar question I posed

Detecting unicorn and dinosaur compilers

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.