Search code examples
c++floating-pointdoubleieee-754

IEEE 754/iec 559


Is the IEEE 754 floating point format well defined across platforms? In terms of both bit format and endianness?

I am willing to add the following to my code (for an initial version):

static_assert(std::numeric_limits<float>::is_iec559, "Only support IEC 559 (IEEE 754) float");
static_assert(sizeof(float) * CHAR_BIT == 32, "Only support float => Single Precision IEC 559 (IEEE 754)");

static_assert(std::numeric_limits<double>::is_iec559, "Only support IEC 559 (IEEE 754) double");
static_assert(sizeof(float) * CHAR_BIT == 64, "Only support double => Double Precision IEC 559 (IEEE 754)");

static_assert(std::numeric_limits<long double>::is_iec559, "Only support IEC 559 (IEEE 754) long double");
static_assert(sizeof(float) * CHAR_BIT == 128, "Only support long double  => Exteneded Precision IEC 559 (IEEE 754)");
//  More asserts if required.
//  I noticed my current system has a sizeof(long double) => 128
//  But numeric_limits<long double>::digits  => 63
//  So we are not storing quad precision floats only extended.

If I write my float/double/long double in binary format can these be transported between systems without further interpretation. ie...

void write(std::ostream& stream, double value)
{
     stream.write(reinterpret_cast<char const*>(&value), 8);
}

....

double read(std::istream& stream)
{
     double   value;
     stream.read(reinterpret_cast<char*>(&value), 8);
     return value;
}

Or do I need to break the double up into integer components for transport (as suggested by this answer):

The difference here is I am willing to limit my supported representation to IEEE-754 will this basically solve my binary storage of floating point values or do I need to take further steps?

Note: For non conforming platforms (when I find them) I am willing to special case the code so that they read/write IEEE-754 into local representation. But I want to know if the bit/endian is well enough defined cross platform to support storage/transport.


Solution

  • Bit format is well-defined, but not all machines are little-endian. The IEEE standard does not require floating-point numbers to be a certain endian, either. You can run the following program to see the byte pattern of the double 42.0:

    #include <stdio.h>
    #include <numeric>
    #include <limits>
    using namespace std;
    
    int main() {
      double d = 42;
      printf("%i\n", std::numeric_limits<double>::is_iec559);
      for (char *c = (char *)&d; c != (char *)(&d+1); c++) {
        printf("%02hhx ", *c);
      }
      printf("\n");
    }
    

    On an old, unmaintained Sun machine using g++ 3.4.5, this prints

    1
    40 45 00 00 00 00 00 00
    

    On an x86_64 machine running a much more recent g++:

    1
    00 00 00 00 00 00 45 40