Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Ordinarily all of the mantissa bits are used to express significant figures in addition to a leading 1 which is implied and therefore left out. Thus floats ordinarily have 24 significant bits of precision and doubles ordinarily have 53 significant bits of precision. Such numbers are called normalized numbers. All floating point numbers are limited in this sense that they have fixed precision. FLP00-C. Understand the limitations of floating point numbers.

However, to express extremely small numbers that are too small to encode normally because not enough exponent bits are available mantissa bits are used to extend the possible range of exponents. These bits no longer function as significant bits of precision so the total precision of extremely small numbers is smaller than usual. Such numbers are called denormalized. Using even normalized numbers where precision is required can pose a risk FLP02-C. Avoid using floating point numbers when precise computation is needed, but denormalized numbers are vastly more limited than one would expect from dealing only with normalized values.

Using denormalized numbers can severely impair the precision of floating point numbers and should not be used.
FLP00-C. Understand the limitations of floating point numbers
FLP02-C. Avoid using floating point numbers when precise computation is needed

Noncompliant Code Example

...

If using doubles also produces denormalized numbers some other solution must be found.

Printing denormalized numbers

Denormalized numbers can also be troublesome because some functions have implementation defined behavior when used with denormalized values. For example, using the %a or $%A conversion specifier in a format string can produce implementation defined results when applied to denormalized numbers.

...

Relying on the %a and %A specifiers to produce values without a leading zero is error prone.

Code Block

#include<stdio.h>
float x = 0x1p-125;
double y = 0x1p-1020;
printf("normalized float with %%e    : %e\n", x);
printf("normalized float with %%a    : %a\n", x);
x = 0x1p-140;
printf("denormalized float with %%e  : %e\n", x);
printf("denormalized float with %%a  : %a\n", x);
printf("normalized double with %%e   : %e\n", y);
printf("normalized double with %%a   : %a\n", y);
y = 0x1p-1050;
printf("denormalized double with %%e : %e\n", y);
printf("denormalized double with %%a : %a\n", y);

Implementation Details

On a 32-bit Linux machine using gcc version 4.3.2 this code produces the following output.

Code Block

normalized float with %e    : 2.350989e-38
normalized float with %a    : 0x1p-125
denormalized float with %e  : 7.174648e-43
denormalized float with %a  : 0x1p-140
normalized double with %e   : 8.900295e-308
normalized double with %a   : 0x1p-1020
denormalized double with %e : 8.289046e-317
denormalized double with %a : 0x0.0000001p-1022

Risk Assessment

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

FLP05-C

medium

probable

high

P4

L3

...