You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 47 Next »

The C programming language provides the ability to use floating-point numbers for calculations. The C standard specifies requirements on a conforming implementation for floating-point numbers but makes few guarantees about the specific underlying floating-point representation because of the existence of competing floating-point systems.

By definition, a floating-point number is of finite precision and, regardless of the underlying implementation, is prone to errors associated with rounding. (See FLP01-C. Take care in rearranging floating point expressions and FLP02-C. Avoid using floating point numbers when precise computation is needed.)

The most common floating-point system is specified by the IEEE 754 standard. An older floating-point system is the IBM floating-point representation (sometimes called IBM/370). Each of these systems has different precisions and ranges of representable values. As a result, they do not represent all of the same values, are not binary compatible, and have different associated error rates.

Because of a lack of guarantees on the specifics of the underlying floating-point system, no assumptions can be made about either precision or range. Even if code is not intended to be portable, the chosen compiler's behavior must be well understood at all compiler optimization levels.

Here is a simple illustration of precision limitations. The following code prints the decimal representation of 1/3 to 50 decimal places. Ideally, it would print 50 numeral 3s.

#include <stdio.h>

int main(void) {
  float f = 1.0 / 3.0;
  printf("Float is %.50f\n", f);
  return 0;
}

On 64-bit Linux, with GCC compiler 4.1, this produces

Float is 0.33333334326744079589843750000000000000000000000000

On Windows XP, with Microsoft Visual C++ Compiler 9.0, this produces

Float is 0.33333334326744080000000000000000000000000000000000

Additionally, compilers may treat floating-point variables differently under different levels of optimization [Gough 2005].

double a = 3.0;
double b = 7.0;
double c = a / b;

if (c == a / b) {
  printf("Comparison succeeds\n");
} else {
  printf("Unexpected result\n");
}

When compiled on an IA-32 Linux machine with GCC compiler 3.4.4 at optimization level 1 or higher or on a test IA-32 Windows XP machine with Microsoft Visual C++ Express 8.0, this code prints

Comparison succeeds

On an IA-32 Linux machine with GCC compiler 3.4.4 with optimization turned off, this code prints

Unexpected result

The reason for this behavior is that Linux uses the internal extended precision mode of the x87 floating-point unit (FPU) on IA-32 machines for increased accuracy during computation. When the result is stored into memory by the assignment to c, the FPU automatically rounds the result to fit into a double. The value read back from memory now compares unequally to the internal representation, which has extended precision. Windows does not use the extended precision mode, so all computation is done with double precision, and there are no differences in precision between values stored in memory and those internal to the FPU. For GCC, compiling at optimization level 1 or higher eliminates the unnecessary store into memory, so all computation happens within the FPU with extended precision [Gough 2005].

Risk Assessment

Failing to understand the limitations of floating-point numbers can result in unexpected computational results and exceptional conditions, possibly resulting in a violation of data integrity.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

FLP00-C

medium

probable

high

P4

L3

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this recommendation on the CERT website.

Related Guidelines

Bibliography

  • No labels