4

I'm trying to run some tests with conversions and castings of floats to other types and I want to set my float variable to different values of nan.

"a bit-wise example of a IEEE floating-point standard single precision (32-bit) NaN would be: s111 1111 1axx xxxx xxxx xxxx xxxx xxxx where s is the sign (most often ignored in applications), a determines the type of NaN, and x is an extra payload (most often ignored in applications). If a = 1, it is a quiet NaN; if a is zero and the payload is nonzero, then it is a signaling NaN"

Basically I want to find a way to set the payload or xxxx's of the representation. Is there any way to do this in c?

Chris
  • 753
  • 2
  • 8
  • 22

3 Answers3

8

You may be able to control the 'payload' bits by passing appropriate strings to the C99 nan, nanf, nanl functions, but these will only generate quiet NaNs, and the interpretation of the string is left unspecified (most implementations treat it as a hexadecimal number).

Alternatively, use a union:

#ifndef __STDC_IEC_559__
#error "This program requires IEEE floating point arithmetic"
#endif

#include <stdint.h>
#include <assert.h>

static_assert(sizeof(float) == sizeof(uint32_t),
    "This program requires float to be 32 bits exactly");

float nanf_with_payload_bits(uint32_t payload)
{
   if (payload & 0x7FA00000) abort();

   union ieee_single {
       float f;
       uint32_t i;
   } nan;

   nan.i = 0x7FA00000 | payload;
   return nan.f;
}

Writing to one member of a union and then reading from another, when both types are exactly the same size, does NOT provoke undefined behavior in C99+errata. (It was undefined behavior in C89, but most compilers defined it to do what you would expect. It may still be undefined behavior in C++, I'm not sure; however, again, most compilers define it to do what you would expect.)

If you use this function to create signaling NaNs, be aware that their behavior is explicitly left undefined in C99/C11 Annex F.

DO NOT attempt to break down the i component of the union into a structure with bit-fields. The memory layout of bit-fields within a structure is partially implementation-defined and partially unspecified, and in particular a sequence of bit-fields is not necessarily packed into a word in the same order as the CPU endianness (or, indeed, properly packed at all).


Standards citations (all C99):

  • this use of a union is only unspecified behavior: 6.2.6.1p7; J.1
  • the layout of bitfields within a structure is unpredictable: 6.2.6.1p1,2; 6.7.2.1p10,11,13; J.3.9
  • the behavior of signaling NaNs is undefined: F.2.1
zwol
  • 135,547
  • 38
  • 252
  • 361
  • What exactly did you mean by the endianness madness? I might be having a problem related to that. – Chris Apr 28 '12 at 17:59
  • 1
    The short version is, the order of bitfields within a `struct` doesn't necessarily agree with *anything else*. However, the order of bits within a `uint32_t` will be the same as the order of bits within a `float` on all modern systems (there used to be computers where they were not the same, but AFAIK none have been manufactured in quite some time). The long version is much too long for this box. I'd have to see the code that's causing you a problem to say anything more. Suggest you ask a new question about that and link it here. – zwol Apr 28 '12 at 18:14
  • Just linked it: http://stackoverflow.com/questions/10366485/problems-casting-nan-floats-to-int Thanks! – Chris Apr 28 '12 at 18:45
5

Use memcpy:

int32_t i = 0x7FC00000;
float f;
memcpy(&f, &i, sizeof(f));

You could also assert that sizeof(f) == sizeof(i), but if you know that floats are IEEE then presumably you also know what size the basic types are.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
3

There is a supported way to write the payload of a quiet NaN in C.

The nan, nanf, and nanl functions (of the math.h header, section 7.12.11.2 of the 1999 C specification) accept strings as arguments. The strtof, strtod, and strtold functions (of the stdlib.h header, section 7.20.1.3) accept strings of the form "NAN(character sequence)". The fscanf and sscanf functions follow strtod. However, the character sequence is interpreted in an implementation-defined way. (Which means your compiler is supposed to provide you with documentation specifying the interpretation. Some compilers will not obey this requirement of the standard.)

The fprintf function (stdio.h, section 7.19.6.1) may output a string of the form "NAN(character sequence)" with the floating-point formats (a, e, f, g), as may printf and sprintf. The standard permits the output of "NAN" with no character sequence, so this will not work with many compilers.

Because the interpretations are implementation-defined, you should not expect them to be portable. They are typically intended for special purposes such as debugging.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312