How to guarantee exact size of double in C?

Viewed 514

So, I am aware that types from the stdint.h header provide standardized width integer types, however I am wondering what type or method does one uses to guarantee the size of a double or other floating point type across platforms? Specifically, this would deal with packing data in a void*

#include <stdio.h>
#include <stdlib.h>

void write_double(void* buf, double num)
{
  *(double*)buf = num;
}

double read_double(void* buf)
{
  return *(double*)buf;
}


int main(void) {
  void* buffer = malloc(sizeof(double));
  write_double(buffer, 55);
  printf("The double is %f\n", read_double(buffer));
  return 0;
}

Say like in the above program, if I wrote that void* to a file or if it was used on another system, would there be some standard way to guarantee size of a floating point type or double?

4 Answers

How to guarantee exact size of double in C?

Use _Static_assert()

#include <limits.h>

int main(void) {
  _Static_assert(sizeof (double)*CHAR_BIT == 64, "Unexpected double size");
  return 0;
}

_Static_assert available since C11. Otherwise code could use a run-time assert.

#include <assert.h>
#include <limits.h>

int main(void) {
  assert(sizeof (double)*CHAR_BIT == 64);
  return 0;
}

Although this will insure the size of a double is 64, it does not insure IEEE 754 double-precision binary floating-point format adherence.

Code could use __STDC_IEC_559__

An implementation that defines __STDC_IEC_559__ shall conform to the specifications in this annex` C11 Annex F IEC 60559 floating-point arithmetic

Yet that may be too strict. Many implementations adhere to most of that standard, yet still do no set the macro.


would there be some standard way to guarantee size of a floating point type or double?

The best guaranteed is to write the FP value as its hex representation or as an exponential with sufficient decimal digits. See Printf width specifier to maintain precision of floating-point value

The problem with floating point type is that the C standard doesn't specify how they should be represented. The use of IEEE 754 is not required.

If you're communicating between a system that uses IEEE 754 and one that doesn't, you won't be able to write on one and read on the other even if the sizes are the same.

You need to serialize the data in a known format. You can either use sprintf to convert it to a text format, or you can do some math to determine the base and mantissa and store those.

Floating point values are defined in the The IEEE Standard for Floating-Point Arithmetic (IEEE 754) and have standard sizes:

The following also exist:

This format is reused in the C11 standard, Annex F "IEC 60559 floating-point arithmetic" of ISO/IEC 9899:2011(en).

Why use CHAR_BIT and assert at runtime? We can do this at compile time.

void write_double(void* buf, double num)
{
   char checkdoublesize[(sizeof(double) == 8)?1:-1];
   *(double*)buf = num;
}

Your code is still undefined as it doesn't gurantee IEEE or endianness but it will catch a bad double size. If your platform's new enough for htonq this will allow endianness to work

void write_double(void* buf, double num)
{
   char checkdoublesize[(sizeof(double) == 8)?1:-1];
   *(int64_t*)buf = htonq(*(volatile int64_t*)&num);
}


double read_double(void* buf)
{
   int64_t n = ntohq(*(int64_t*)buf);
   return *(volatile double*)&n;
}

Where volatile is merely the shortest way to tell the compiler the pointer cast really is defined. Usually it does the right thing anyway but after N levels of inlining maybe it won't anymore.

Related