Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions

  




 

 

8.6 Floating-point issues

The IEEE-754 standard defines the bit-level behavior of floating-point arithmetic operations on all modern processors. This allows numerical programs to be ported between different platforms with identical results, in principle. In practice, there are often minor variations caused by differences in the order of operations (depending on the compiler and optimization level) but these are generally not significant.

However, more noticeable discrepancies can be seen when porting numerical programs between x86 systems and other platforms, because the the x87 floating point unit (FPU) on x86 processors computes results using extended precision internally (the values being converted to double precision only when they are stored to memory). In contrast, processors such as SPARC, PA-RISC, Alpha, MIPS and POWER/PowerPC work with native double-precision values throughout.(26) The differences between these implementations lead to changes in rounding and underflow/overflow behavior, because intermediate values have a greater relative precision and exponent range when computed in extended precision.(27) In particular, comparisons involving extended precision values may fail where the equivalent double precision values would compare equal.

To avoid these incompatibilities, the x87 FPU also offers a hardware double-precision rounding mode. In this mode the results of each extended-precision floating-point operation are rounded to double precision in the floating-point registers by the FPU. It is important to note that the rounding only affects the precision, not the exponent range, so the result is a hybrid double-precision format with an extended range of exponents.

On BSD systems such as FreeBSD, NetBSD and OpenBSD, the hardware double-precision rounding mode is the default, giving the greatest compatibility with native double precision platforms. On x86 GNU/Linux systems the default mode is extended precision (with the aim of providing increased accuracy). To enable the double-precision rounding mode it is necessary to override the default setting on per-process basis using the FLDCW "floating-point load control-word" machine instruction.(28) A simple function which can be called to execute this instruction is shown below. It uses the GCC extension keyword asm to insert the specified instruction in the assembly language output:

void 
set_fpu (unsigned int mode)
{
  asm ("fldcw %0" : : "m" (*&mode));
}

The appropriate mode setting for double-precision rounding is 0x27F. The mode value also controls the floating-point exception handling behavior and rounding-direction (see the Intel and AMD processor reference manuals for details).

On x86 GNU/Linux, the function above can be called at the start of any program to disable excess precision. This will then reproduce the results of native double-precision processors, in the absence of underflows and overflows.

The following program demonstrates the different rounding modes:

#include <stdio.h>

void 
set_fpu (unsigned int mode)
{
  asm ("fldcw %0" : : "m" (*&mode));
}

int
main (void)
{
  double a = 3.0, b = 7.0, c;
#ifdef DOUBLE
  set_fpu (0x27F);  /* use double-precision rounding */
#endif
  c = a / b;

  if (c == a / b) {
    printf ("comparison succeeds\n");
  } else {
    printf ("unexpected result\n");
  }
  return 0;
}

On x86 GNU/Linux systems the comparison c == a / b can produce an unexpected result if c is taken from memory (double precision) while a / b is computed in extended precision, because the fraction 3/7 has different representations in double and extended precision.

$ gcc -Wall fptest.c
$ ./a.out 
unexpected result

Setting the hardware rounding mode to double precision prevents this from happening:

$ gcc -Wall -DDOUBLE fptest.c
$ ./a.out 
comparison succeeds

Note that the floating-point control word affects the whole environment of the process, including any C Library functions that are called. One consequence of this is that long double arithmetic is effectively reduced to double precision, since it relies on extended precision operations.

The floating point control word only affects the behavior of the x87 FPU. Floating point operations computed with SSE and SSE2 instructions are always carried out in native double precision. Thus, the combined options

$ gcc -Wall -msse2 -mfpmath=sse  ...

are often sufficient to remove the effects of extended-precision. However, some operations (such as transcendental functions) are not available in the SSE/SSE2 extensions and will still be computed on the x87 FPU.


 
 
  Published under the terms of the GNU General Public License Design by Interspire