J
jacob navia
Hi people
I continue to work in the tutorial for lcc-win32, and started to try to
explain the floating point flags.
Here is the relevant part of the tutorial. Since it is a difficult part,
I would like your expert advise before I publish any serious nonsense.
Any comments are welcome, style, organization, hard errors, etc.
Thanks in advance
jacob
-------------------------------------------------------------------
Using the floating point environment
------------------------------------
The C standard specifies a type fenv_t that “refers collectively to any
floating-point status flags and control modes supported by the
implementation.”
This environment has two special parts, the “floating point status
flags” that are set by the operations the program performs, and the
“control flags” that changes how the hardware does the operations, for
instance the rounding mode, etc.
The status flags
----------------
The standard requires at least the following flags:
FE_DIVBYZERO A division by zero has occurred
FE_INEXACT The last operation had to be rounded
FE_INVALID An invalid operation was attempted
FE_OVERFLOW The result of an operation was too big to be represented
FE_UNDERFLOW The result of an operation was too small to be represented
In addition to this standard flags, lcc-win32 provides:
FE_DENORMAL The result of the last operation was a denormal number
FE_STACKFAULT The floating point stack has overflowed.
The denormal flag means that a loss of precision is certain, the number
is too small to be represented. The stack fault flag means that
lcc-win32 generated bad code, since all floating point operations should
balance the floating point stack. If you ever test positive for this
flag, do not hesitate to send me a bug report!
In general, the floating point flags can be queried by the program to
know if the last operation did complete without problems. Here is a
small example to show you how this works:
/* This program tests a division by zero */
#include <fenv.h>
#include <stdio.h>
int main(void)
{
double a=1,b=0;
feclearexcept(FE_DIVBYZERO);
a=a/b;
if (fetestexcept(FE_DIVBYZERO)) {
printf("You have divided by zero!\n");
}
return 0;
}
First we clear the flag that we are going to use using feclearexcept.
Then, we perform our operation and query if the flag is set using
fetestexcept.
Since we know that the flags are set but not cleared, the expression
could be very well be a much more complicated sequence of operations.
The above code would work the same, but we would loose the possibility
of knowing exactly which operation failed. This is in many cases not
very important, we could very well be interested that somewhere there
was a serious error, without bothering to investigate which operation
was that it failed.
Reinitializing the floating point environment
---------------------------------------------
Things can become messy, and you would like to reset everything to a
known state. The standard provides the macro FE_DFL_ENV, that represents
the address of the default environment, the one you started with. In
lcc-win32 this environment is stored in the __default_fe_env global
variable, so this macro is just:
#define FE_DFL_ENV (&__default_fe_env)
You can reset everything to its default state with:
fesetenv(FE_DFL_ENV);
The default environment in lcc-win32 has the following characteristics:
1) The rounding mode is set to round to nearest.
2) The precision of the FPU is set to full precision (80 bits)
3) All exceptions are masked, i.e. the result of invalid operations is a
NAN, not a trap.
---------------------------------------------------------------------------
I continue to work in the tutorial for lcc-win32, and started to try to
explain the floating point flags.
Here is the relevant part of the tutorial. Since it is a difficult part,
I would like your expert advise before I publish any serious nonsense.
Any comments are welcome, style, organization, hard errors, etc.
Thanks in advance
jacob
-------------------------------------------------------------------
Using the floating point environment
------------------------------------
The C standard specifies a type fenv_t that “refers collectively to any
floating-point status flags and control modes supported by the
implementation.”
This environment has two special parts, the “floating point status
flags” that are set by the operations the program performs, and the
“control flags” that changes how the hardware does the operations, for
instance the rounding mode, etc.
The status flags
----------------
The standard requires at least the following flags:
FE_DIVBYZERO A division by zero has occurred
FE_INEXACT The last operation had to be rounded
FE_INVALID An invalid operation was attempted
FE_OVERFLOW The result of an operation was too big to be represented
FE_UNDERFLOW The result of an operation was too small to be represented
In addition to this standard flags, lcc-win32 provides:
FE_DENORMAL The result of the last operation was a denormal number
FE_STACKFAULT The floating point stack has overflowed.
The denormal flag means that a loss of precision is certain, the number
is too small to be represented. The stack fault flag means that
lcc-win32 generated bad code, since all floating point operations should
balance the floating point stack. If you ever test positive for this
flag, do not hesitate to send me a bug report!
In general, the floating point flags can be queried by the program to
know if the last operation did complete without problems. Here is a
small example to show you how this works:
/* This program tests a division by zero */
#include <fenv.h>
#include <stdio.h>
int main(void)
{
double a=1,b=0;
feclearexcept(FE_DIVBYZERO);
a=a/b;
if (fetestexcept(FE_DIVBYZERO)) {
printf("You have divided by zero!\n");
}
return 0;
}
First we clear the flag that we are going to use using feclearexcept.
Then, we perform our operation and query if the flag is set using
fetestexcept.
Since we know that the flags are set but not cleared, the expression
could be very well be a much more complicated sequence of operations.
The above code would work the same, but we would loose the possibility
of knowing exactly which operation failed. This is in many cases not
very important, we could very well be interested that somewhere there
was a serious error, without bothering to investigate which operation
was that it failed.
Reinitializing the floating point environment
---------------------------------------------
Things can become messy, and you would like to reset everything to a
known state. The standard provides the macro FE_DFL_ENV, that represents
the address of the default environment, the one you started with. In
lcc-win32 this environment is stored in the __default_fe_env global
variable, so this macro is just:
#define FE_DFL_ENV (&__default_fe_env)
You can reset everything to its default state with:
fesetenv(FE_DFL_ENV);
The default environment in lcc-win32 has the following characteristics:
1) The rounding mode is set to round to nearest.
2) The precision of the FPU is set to full precision (80 bits)
3) All exceptions are masked, i.e. the result of invalid operations is a
NAN, not a trap.
---------------------------------------------------------------------------