I get into a situation where calculating 1.77e-308/10
triggers an underflow exception, but calculating 1.777e-308/10
does not. This is strange because:
Underflow occurs when the true result of a floating point operation is smaller in magnitude (that is, closer to zero) than the smallest value representable as a normal floating point number in the target datatype (from Arithmetic Underflow, Wikipedia)
In other words, if we calculate x/y
where both x
and y
are double
, then underflow should occur if 0 < |x/y| < 2.2251e-308
(the smallest positive normalized double
is 2.2251e-308
). In theory, therefore, both 1.77e-308/10
and 1.777e-308/10
should trigger an underflow exception. The theory contradicts with what I have tested with the C program below.
#include <stdio.h>
#include <fenv.h>
#include <math.h>
int main(){
double x,y;
// x = 1.77e-308 => underflow
// x = 1.777e-308 gives ==> no underflow
x=1.77e-308;
feclearexcept(FE_ALL_EXCEPT);
y=x/10.0;
if (fetestexcept(FE_UNDERFLOW)) {
puts("Underflow\n");
}
else puts("No underflow\n");
}
To compile the program, I used gcc program.c -lm
; I also tried Clang, which gave me the same result. Any explanation?
[Edits] I have shared the code above via this online IDE.
Checking the documentation for the function that you called, leads to the definition:
http://en.cppreference.com/w/c/numeric/fenv/FE_exceptions
You've verified that your number is subnormal, I think. The test also includes loss of precision. If you print more significant figures, you'll find that the one reporting the overflow does seem to lose precision at about 16 decimal places. I'm not clear how many significant figure to expect on a subnormal number, but I think this must be your answer.
Underflow is not only a question of range, but also of precision/rounding.
1.777e-308, converted to the nearest binary64 0x1.98e566222bcfcp-1023, happens to have a significand (0x198E566222BCFC, 7193376082541820) that is a multiple of 10. So dividing by 10 is exact. No roundoff error.
I find this easier to demo with hex notation. Note that dividing by 2 is always exact, except for the smallest value.
Output