cloopsfloating-pointcomputer-sciencesigfpe

What is the difference between underflow and nan in C?


Currently I'm learning about floating point exceptions. I'm writing a loop with a function. In that function a value is calculated that equals 0.5. As the loop proceeds, the input value gets divided by 10.

The loop:

 for(i = 0; i < e; i++)
 {
     xf /= 10.0;   // force increasingly smaller values for x
     float_testk (xf, i);
 }

The function:

void float_testk(float x, int i)
{
    float result;
   
    feclearexcept(FE_ALL_EXCEPT); // clear all pending exceptions
         
    result = (1 - cosf(x)) / (x * x);
        
    if(fetestexcept(FE_UNDERFLOW) != 0)
        fprintf(stderr,"Underflow occurred in double_testk!\n");
    if(fetestexcept(FE_OVERFLOW) != 0)
        fprintf(stderr,"Overflow occurred in double_testk!\n");
    if(fetestexcept(FE_INVALID) != 0)
        fprintf(stderr,"Invalid exception occurred in double_testk!\n");
   
    printf("Iteration %3d, float result for x=%.8f : %f\n",i,x,result);
}

The first few iterations the output is around 0.5 and later it becomes 0 due CC. After a while this is the output of the program:

Iteration  18, float result for x=0.00000000 : 0.000000
Underflow occurred in double_testk!
Iteration  19, float result for x=0.00000000 : 0.000000
Underflow occurred in double_testk!
Iteration  20, float result for x=0.00000000 : 0.000000
Underflow occurred in double_testk!
Iteration  21, float result for x=0.00000000 : 0.000000
Underflow occurred in double_testk!
Invalid exception occurred in double_testk!
Iteration  22, float result for x=0.00000000 : -nan
Underflow occurred in double_testk!
Invalid exception occurred in double_testk!

I want to know what happens at the transition from underflow to NaN. Because underflow means that the number is too small to be stored in the memory.

But if the number is already too small, what is the goal of NaN?


Solution

  • NaN is a concept defined in IEEE 754 standard for floating-point arithmetic, not being a number is not the same as negative infinity or positive infinity, NaN is used for arithmetic values that cannot be represented, not because they are too small or too large but simply because they don't exist. Examples:

    1/0 = ∞ //too large     
    log (0) = -∞ //too small
    sqrt (-1) = NaN //is not a number, can't be calculated
    

    IEEE 754 floating point numbers can represent positive or negative infinity, and NaN (not a number). These three values arise from calculations whose result is undefined or cannot be represented accurately. You can also deliberately set a floating-point variable to any of them, which is sometimes useful. Some examples of calculations that produce infinity or NaN:

    The goal of these flags you are using is to be compiant with the mentioned standard. It specifies five arithmetic exceptions that are to be recorded in the status flags:

    FE_INEXACT: Inexact result, rounding was necessary to store the result of an earlier floating-point operation.

    Set if the rounded (and returned) value is different from the mathematically exact result of the operation.

    FE_UNDERFLOW: The result of an earlier floating-point operation was subnormal with a loss of precision.

    Set if the rounded value is tiny (as specified in IEEE 754) and inexact (or maybe limited to if it has denormalization loss, as per the 1984 version of IEEE 754), returning a subnormal value including the zeros.

    FE_OVERFLOW: The result of an earlier floating-point operation was too large to be representable.

    Set if the absolute value of the rounded value is too large to be represented. An infinity or maximal finite value is returned, depending on which rounding is used.

    FE_DIVBYZERO: Pole error occurred in an earlier floating-point operation.

    Set if the result is infinite given finite operands, returning an infinity, either +∞ or −∞.

    FE_INVALID: Domain error occurred in an earlier floating-point operation.

    Set if a real-valued result cannot be returned e.g. sqrt(−1) or 0/0, returning a quiet NaN.*

    *Concept of quiet NaN:

    Quiet NaNs, or qNaNs, do not raise any additional exceptions as they propagate through most operations. The exceptions are where the NaN cannot simply be passed through unchanged to the output, such as in format conversions or certain comparison operations.

    Sources:

    Floating point environment

    Floating-point arithmetic

    20.5.2 Infinity and NaN

    NaN