Illegal float values

c, exception, gcc

Solution

Summary

I suggest you print the bits of floating-point values as if they were a hexadecimal integer, as shown in code below, so that you can analyze those bits to see if they contain the values you are attempting to compute or have been modified improperly due to some bug.

Details

The AVR32CU Technical Reference Manual says “The floating point hardware conforms to the requirements of the C standard, which is based on the IEEE 754 floating point standard.“ The latter clause is false; the C standard is not based on IEEE 754. The C standard does specify bindings to IEEE 754 (via the name IEC 60559) as an optional feature of C implementations. I will presume that the model of AVR32 CPU you are using conforms to IEEE 754 to some degree.

There are no “illegal” values in IEEE 754. There are values that do not represent numbers, and some of those values are intended to cause exceptions. Such a value is called a NaN (for “Not a Number”). There are quiet NaNs and signaling NaNs. Quiet NaNs are intended to pass through operations silently, producing a NaN result. E.g., `3 + NaN` should produce `NaN`. Signaling NaNs are intended to cause exceptions, which may cause changes to program control (such as signals or program aborts).

The technical reference manual cited above also says “Signalling NaN are not provided, all NaN are non-signalling (quiet).”

A good `vsnprintf` routine should accept quiet NaN values for printing and should format them by producing a string such as “NaN”. When a signaling NaN is passed for formatting, I suppose it might be reasonable either to format it or to produce an exception.

I expect the message you received from support is suggesting that your software created some kind of NaN, and that `vsnprintf` cannot handle these. From the phrasing, I think their response is speculative.

If you are creating floating-point values by assembling bytes, then you may have created a NaN when you did not intend to, if there was some error in your software. I suggest that you debug this by using `vsnprintf` to print the bytes of the floating-point value instead of printing it with a floating-point format specifier.

If the GCC version you are using has the usual features of GCC, and the `unsigned int` in your implementation is 32 bits, you can format the bits of a 32-bit `float` value `x` as a hexadecimal value using:

vsnprintf(Buffer, BufferLength, "0x%x",
    (union { float f; unsigned int u; }) {x} .u);

The second line uses a compound literal to put the value `x` into a union and reinterpret its bytes as an `unsigned int`. (This is a supported way in C to reinterpret the bytes of an object. Many people use pointer aliasing, which works in GCC if the appropriate flag is used, but it is not generally supported by the C standard. Another supported method is to copy the bytes, as with `unsigned int u; memcpy(&u, &x, sizeof u);`.)

Once you see what the bits in the `float` are, you can interpret them manually from information in the IEEE 754 standard or using an online analyzer. (Select the “hexadecimal” button to input hexadecimal values to be interpreted.)

In an IEEE-754 32-bit binary floating-point object, the value is a NaN if:

- Bits 31 has any value. (It is the sign bit, irrelevant for recognizing a NaN.)

- Bits 30 to 23 are all ones.

- Bits 22 to 0 are not all zeros.

(If Bits 30 to 23 are all ones but bits 22 to 0 are all zeroes, the value is an infinity. This is not illegal but might also cause a low-quality `vsnprintf` to generate an exception.)

Problem

I am currently working on an embedded microcontroller and use a custom printf routine. The toolchain is the GCC Toolchain for the AVR32 architecture. I have the problem that upon calling vsnprintf or similar for the second time that the CPU enters an exception condition. From support, I received the answer that: We could not find any obvious reason for such behavior. However, creating a float overflow condition by writing byte by byte is not safe. We cannot ensure the value generated by this and it is recommended to check using “FLT_MAX”. Now I am wondering: What are "illegal" float values? Shouldn't all bit combinations represent at least some value? If relevant: sizeof(float) is 4 bytes.

Original source