By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,143 Members | 1,854 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,143 IT Pros & Developers. It's quick & easy.

The floating point environment

P: n/a
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.

---------------------------------------------------------------------------
Jan 17 '06 #1
Share this Question
Share on Google+
4 Replies


P: n/a
In article <43***********************@news.wanadoo.fr>,
jacob navia <ja***@jacob.remcomp.fr> wrote:
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
Are you sure it is "the last operation", and not any operation? Does
this flag get cleared again after an operation that doesn't need
rounding?
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.
Do you mean "too small to be represented with full precision"?
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!
I don't like this at all. I wouldn't want to write code that checks if
the compiler is broken. If there is any possibility at all that this
happens, could you make this throw a hardware exception, so that it
definitely will get detected?
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 **** lose of knowing exactly which operation failed. This is in many cases not ******

I wouldn't say that the operaton failed. Most floating-point operations
will set the FE_INEXACT flag, that doesn't mean they failed.
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.

---------------------------------------------------------------------------

Jan 18 '06 #2

P: n/a
Christian Bau a écrit :
In article <43***********************@news.wanadoo.fr>,
jacob navia <ja***@jacob.remcomp.fr> wrote:

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

Are you sure it is "the last operation", and not any operation? Does
this flag get cleared again after an operation that doesn't need
rounding?


The x87 instruction manual specifies that by most arithmetic
operations, i.e. FADD FSUB, etc this flag is set/unset if the
result needed rounding. The internal flags (C0 C1 C3) specify
the direction of the rounding that was performed: up or down.
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.

Do you mean "too small to be represented with full precision"?


Yes, you are right. Thanks.
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!

I don't like this at all. I wouldn't want to write code that checks if
the compiler is broken. If there is any possibility at all that this
happens, could you make this throw a hardware exception, so that it
definitely will get detected?


Yes, it would just mean unmasking the exception flag, what would
provoke a trap if a FP stack overflow happens.
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


**** lose
of knowing exactly which operation failed. This is in many cases not


******

I wouldn't say that the operaton failed. Most floating-point operations
will set the FE_INEXACT flag, that doesn't mean they failed.


True, will correct that.
Thanks for your input.

jacob
Jan 18 '06 #3

P: n/a
jacob navia wrote:
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
It would be much more straight forward to say that the result produced
was not the exact mathematical result of the operation. That avoids the
awkwardness of having to define "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

Nit: The magnitude of the result was too large/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 statement "loss of precision is certain" is accurate, but
misleading. It doesn't imply an erroneous value or loss of accuracy.

A denormalized number is too small to be represented with full
precision, but not too small to be represented at all.
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 suggest: completed ^^^^^^^^^^^^ 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. First we clear the flag FE_DIVBYZERO using feclearexcept.
Then, we perform our operation and query if the flag is set using
fetestexcept. We next perform the divide, then test the flag using fetestexcept.
Since we know that the flags are set but not cleared, the expression xxxxxxxxxxxx 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 xxxx . We might only need to know was a serious error, without bothering to investigate which operation knowing precisely where. was that it failed.


--
Thad
Jan 19 '06 #4

P: n/a
Thad Smith a écrit :
jacob navia wrote:
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

It would be much more straight forward to say that the result produced
was not the exact mathematical result of the operation. That avoids the
awkwardness of having to define "had to be rounded".


OK, it is a better formulation. Will change that.
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

Nit: The magnitude of the result was too large/small to be represented.


Mmmm, if I use magnitude I would have to explain what does that mean
in this context. I do explain in a previous chapter the floating
point format, so I could go back to that and explain overflow

explicitely with normal --> denormal --> overflow
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 statement "loss of precision is certain" is accurate, but
misleading. It doesn't imply an erroneous value or loss of accuracy.

A denormalized number is too small to be represented with full
precision, but not too small to be represented at all.
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


suggest: completed ^^^^^^^^^^^^

Yes, typo. Thanks
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.


First we clear the flag FE_DIVBYZERO using feclearexcept.
Then, we perform our operation and query if the flag is set using
fetestexcept.


We next perform the divide, then test the flag using fetestexcept.

Since we know that the flags are set but not cleared, the expression


xxxxxxxxxxxx
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


xxxx . We might only need to know
> was a serious error, without bothering to investigate which operation

knowing precisely where.
was that it failed.


Jan 19 '06 #5

This discussion thread is closed

Replies have been disabled for this discussion.