4

I'm writing classes for basic types, so that code is logically the same on multiple platforms and compilers (like int_least16_t for int). For fun! (I'm still a student.) And I read this:

float [...] Matches IEEE-754 binary32 format if supported.

And what's worse:

Floating-point types MAY support special values: , NaN or -0

Which means that float MAY be unsigned...
[edit: Yes it's difrent thing, but there is no: ",but must suport negative numbers". Yo, with out things like this in standart it may not suport normal 0...(I don't have specyfication.) see]

I know it's like with __int128, and the standard is just a standard, but still... IEEE-754 is from 1985, but some machines can be weird, and some legacy hardware doesn't have a floating unit.

As I understand, float is mandatory (not optional like int16_t), but can be in any standard, and any set of values can be possible?


Only thing we have are some macros (<cfloat>):

  • FLT_MIN, FLT_MAX - Even if FLT_MIN = IEEE-754::FLT_MIN, float can be non IEEE-754. For example float with: flipped exponent with fraction...

  • FLT_RADIX - Base system? If so, can help to write the exact value. But still, float can be 3 bit or 200 bit (in size)...

  • FLT_EPSILON - (from 1 to next) We might use it (with base) to check fraction size...

  • FLT_MANT_DIG - Is it "mantissa" digits / fraction size?

  • FLT_MAX_EXP - Exponent filled with 1... in IEEE-754, but outside can be a random number?

If float is like IEEE-754 (sign, exponent, fraction), then it's easy, but if -0 and NaN are optional then it MAY be different. Because I can't tell them apart, I can't use bit representation (in a safe manner). And if is optional, float is no longer a safe type.

The only way out I see is to add macro to compiler.

I know it's a theoretical problem, but I'm interested if there is any check possible or we all write implementation dependent code, when we use the float keyword?


Edit 2022 May 04:

I came up with this:

User eg. code:

//User eg. code:

int main()
{
   float_M a = 1f;
   float_M b = 0f;
   std::cout << a/b; //should output infinty (IEEE-754)
}

//Code:

class float_M
{
public:
#ifdef __STDC_IEC_559__
   float data;
//...
   float_M operator/(float_M x){return float_M(data/x.data);}
//...
#else
   /*union{
      float data;
      struct{//For noSign case ("absolutly catastrofic" case)
         uint_least8_t sign : 1;
         uint_least8_t exponent : 8;
         uint_least32_t fraction : 23;
      }
   }*/ //no noSign case 
   float data;
//...
   float_M operator/(float_M x){return divide(this, x);}

//funtion pointer alert!
   static /*const (1*) */ float_M (*divide)(float_M a, float_M b) =
      /*std::numeric_limits<float>::is_signed ?(*/
         std::numeric_limits<float>::has_infinity ?(
            std::numeric_limits<float>::has_quiet_NaN ?(
               []{return float_M(a.data/b.data);}
            ): &_divide_noNaN
         ): &_divide_noNaN
      /*): &_divide_noSign*/
//...
#endif
}

It's ugly (have funtion pointer), but prevents unnesesery jumps at runtime. I hope c++23 will have better macros.

Also, more links:

Follow-up: Can floats not suport negative

  • 1
    See https://stackoverflow.com/questions/2724359/are-there-any-modern-platforms-with-non-ieee-c-c-float-formats – Barmar Apr 27 '22 at 16:43
  • Note that IEEE-754 was updated in 2008. The major changes related to floating-point decimal arithmetic, though there were probably other cleanups. – Jonathan Leffler Apr 27 '22 at 16:50
  • `so that code is logicly the same on multiple platforms and compilators (like int_least16_t for int)` Err... `std::int_least16_t` is an example of code that is *not* logically the same on multiple platforms. – eerorika Apr 27 '22 at 17:03
  • @eerorika , can you tell more? `std::int_least16_t` can be 18bits so bit operations will be logiclly difrent, but `int_least` is for holding numbers of predictable size. `int` literal grows in size with years, so it's not `at least 16` but 32 or more...? ...or am I wrong...? – Alex Wąsowicz Apr 27 '22 at 18:37
  • 2
    @AlexWąsowicz `int` is at least 16 bits. It may be more than 16 bits. It's usually the same as the "word" size of the CPU, although that doesn't mean much in the context of CPU architectures that have multiple word sizes (e.g. modern x86). `std::int_least16_t` is the smallest integer type that is at least 16 bits. It may be more than 16 bits on architectures that don't have 16 bit types. – eerorika Apr 27 '22 at 18:42
  • is_iec559, Proper adherence to `is_iec559` is challenging for a compiler to test such that a compiler may adhere to `iec559` and not set that true - not worth the testing cost to be 100% compliant. IOWs, `is_iec559 == false` doesn't mean implementation is not compliant. – chux - Reinstate Monica Apr 27 '22 at 19:51
  • " I can't use bit representation (in a safe manner)." --> I think this get to the root issue. Why do you think you need this? Consider posting the higher level problem you are attempting to find a solution - it may not require a certain bit representation. There is a good change your code is not compliant even if `is_iec559()` is true. – chux - Reinstate Monica Apr 27 '22 at 19:55
  • @chux-ReinstateMonica that's good question, I cosider it because it can be done in raw c. But after reading answers, I thing it's best to use it like "black box" end just use math (like log to get exponent). I will just make casting from float_t to bits_t illegal. I will try to make some math functions. But I still don't know what to do with posible math errors (no infinity or NaN value problem). – Alex Wąsowicz Apr 28 '22 at 16:21
  • @AlexWąsowicz See `frexpf()` to break a floating-point number into a normalized fraction and an integral power of 2. – chux - Reinstate Monica Apr 28 '22 at 18:22
  • 2
    "Which means that float MAY be unsigned": no it doesn't. It means it can support negative zero. Not the same thing at all. – user207421 May 04 '22 at 01:23
  • @chux-ReinstateMonica Out of curiosity: do you know about any C implementation using non-IEEE 754 floating-point arithmetic? E.g. based on Unum. – pmor May 11 '22 at 22:06
  • @pmor CCS used to use [this](https://www.ccsinfo.com/faq.php?page=mchp_float_format). – chux - Reinstate Monica May 11 '22 at 22:30
  • @pmor Note that "non-IEEE 754 floating-point arithmetic" could be asserted if even 1 of the 100s of IEEE 754 requirements were not met. I suspect today, many implementation only support most of IEEE 754 and not all. Many of the rounding modes, sub-normal and other esoteric parts are difficult. – chux - Reinstate Monica May 11 '22 at 22:35
  • Re: "if even 1 of the 100s of IEEE 754 requirements were not met": yes, exactly. In that case it should be "_completely_ non-IEEE 754 floating-point arithmetic". I wonder if formally verified FPUs (e.g. by Intel) are (fully) IEEE 754 conforming. – pmor May 12 '22 at 13:32

1 Answers1

6

In C++, the value of std::numeric_limits<T>::is_iec559 shall be true for all floating-point types T "if, and only if, the type adheres to ISO/IEC/IEEE 60559" and ISO/IEC/IEEE 60559:2011 is the same as IEEE 754-2008, so:

#include <iostream>
#include <limits>

int main() {
    std::cout << std::boolalpha << std::numeric_limits<float>::is_iec559 << '\n';
}

Note: As noted in the comments, some implementations may still report true for this constant even though their floating point types are not following the IEEE 754-2008 standard to the letter.

For example, in gcc, you can compile with the options -Ofast or -ffast-math which in turn sets a number of options which can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions.


In C99 (and later), there are conditional feature macros, __STDC_IEC_559__ and __STDC_IEC_559_COMPLEX__, that, if available in your implementation, will tell you if it's conforming to IEC 60559:1989 / IEEE 754−1985.

#include <stdio.h>
int main(void) {
#ifdef __STDC_IEC_559__
    puts("true");
#endif
}

Note that if __STDC_IEC_559__ is not defined, it doesn't necessarily mean that the implementation doesn't use IEEE 754 floats. It could just mean that it doesn't have these conditional feature macros. One interesting note about these macros is that if you use -Ofast or -ffast-math in gcc, they will not be defined (unlike in the C++ test).

The actual revision of the IEC / IEEE standards used changed in C11 and C17/18 and in C23 (draft) there will be a number of new macros related to floating points and it (currently) refers to ISO/IEC 60559:2020 and IEEE 754-2019 which contains minor upgrades to IEC 60559:2011 / IEEE 754-2008.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • Note that this may be true even if compiler is configured to not follow IEEE-754 exactly. – eerorika Apr 27 '22 at 17:00
  • 1
    @eerorika Would that be compliant? [The standard](https://eel.is/c++draft/support.limits#lib:is_iec559,numeric_limits) says _"`true` if and only if the type adheres to ISO/IEC/IEEE 60559."_ and it has a note saying: _"ISO/IEC/IEEE 60559:2011 is the same as IEEE 754-2008._". – Ted Lyngmo Apr 27 '22 at 17:05
  • Technically not. – eerorika Apr 27 '22 at 17:06
  • @eerorika Oh, that's nasty :) Nevertheless, I added a note about it to the answer. Are you thinking about `-Ofast`, `-ffast-math` etc. in gcc/clang? If so I could add a note about that too. – Ted Lyngmo Apr 27 '22 at 17:16
  • 1
    Such optimisations are exactly what I'm thinking. – eerorika Apr 27 '22 at 17:26
  • @eerorika Great! Note added! Thanks for the hint! – Ted Lyngmo Apr 27 '22 at 17:26
  • 1
    For C, you can check the macro `__STDC_IEC_559__`; if it is defined, then the same applies. – Chris Dodd Apr 27 '22 at 21:52
  • @ChrisDodd Thanks! I added some notes about that too. – Ted Lyngmo Apr 27 '22 at 22:31
  • 3
    @Turtlefight: That's now how C++ "Undefined Behavior" works. Where the C++ Standard says that behavior is Undefined, any outcome is acceptable. "Infinity" is therefore also acceptable. it's not just C++ versus IEEE754; you have similar things with C++ and POSIX. – MSalters May 06 '22 at 09:29
  • 1
    @Turtlefight Have a look: [1](https://stackoverflow.com/questions/65189865/how-stdc-iec-559-affects-ub-gaps) and [2](https://stackoverflow.com/questions/65106621/fp-invalid-operation-contradiction-between-c-ub-and-ieee-754-wdb). – pmor May 11 '22 at 22:00
  • @MSalters @​pmor you're right, for most cases compilers are allowed to be IEEE-conforming. One small exception to this seems to be constant expressions, which explicitly disallow most forms of undefined behaviour (including division by zero) ([[expr.const] 5.7](https://timsong-cpp.github.io/cppwp/n4861/expr.const#5.7)) - so it seems like compilers are not allowed to be IEEE-conforming within contexts that require constant expressions (like template parameters, e.g. [godbolt](https://godbolt.org/z/G7qbs69Te)). – Turtlefight Oct 05 '22 at 18:40