How to get around a NaN?

How to get around a NaN?

I have successfully found where our code is trying to access a NaN, using full checking. However, now I know it is there I will fix it later, so what I want to do is move around it to find the next error. So I put in the following code:

if(isnan(tables_pl(i))) then
tables_mlf(i,nzone,temp_loc) = 0.0
else
tables_mlf(i,nzone,temp_loc) = tables_pl(i)
endif

However, I still get a floating point exception trying to access the NaN, but now on the if statement. Why can't the isnan test just return a .true., execute the next line and carry on to the next problem?
Adrian

26 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

I use the following code. Please adapt to your requirements

JFP_CLASS = FP_CLASS(FPLEN(JTETH1))
SELECT CASE (JFP_CLASS)
CASE (0)
call DOSTOP('TOSSF21 FOR_K_FP_SNAN')
CASE(1)
call DOSTOP('TOSSF21 FOR_K_FP_QNAN')
CASE(2)
call DOSTOP('TOSSF21 FOR_K_FP_POS_INF')
CASE(3)
call DOSTOP('TOSSF21 FOR_K_FP_NEG_INF')
CASE(4)
! OK - FOR_K_FP_POS_NORM
CASE(5)
! OK - FOR_K_FP_NEG_NORM
CASE(6)
call DOSTOP('TOSSF21 FOR_K_FP_POS_DENORM')
CASE(7)
call DOSTOP('TOSSF21 FOR_K_FP_NEG_DENORM')
CASE(8)
! OK - FOR_K_FP_POS_ZERO
CASE(9)
! OK - FOR_K_FP_NEG_ZERO ? Saw this with 0. * 9.900990099009901E-003
! ? could have been -0. * 9.900990099009901E-003
CASE DEFAULT
call DOSTOP('TOSSF21 FOR_K_FP_unknown')
END SELECT

The subroutine DOSTOP is a routine I call in lieu of performing a STOP (which is too hard to perform tracebacks). When debugging I keep a breakpoint in the suvroutine DOSTOP and it makes for an easy bug detection.

! common routine to perform CALL DOSTOP()
SUBROUTINE DOSTOP(CVAR)

CHARACTER*(*) CVAR

! place break point here
WRITE(IOUERR,*) CVAR
STOP 'DOSTOP'
! If you wish to continue use debugger to...
! Set Next Statement here
RETURN
END SUBROUTINE DOSTOP

Jim Dempsey

Not sure when I would call this stuff. If I call it before my isnan test, I have no exception. If I call it after, the exception has already occurred.

What I really need is for the code to go on past this NaN exception. I thought that's what isnan() would do - Steve am I right?

Adrian

Hello

You couldtake out this code (with NaN) and compile it in a separate file without any runtime checking, that is, typically full optimization and the /nocheck option. The rest of you files are with full error checking /check:all and all the other useful traps that have been added recently (read documentation).

Lars Petter

Yes, I can do that, but it would be better if isnan() worked.
Adrian

>>

Not sure when I would call this stuff. If I call it before my isnan test, I have no exception. If I call it after, the exception has already occurred.

<<

You call this in place of isnan(YourVariableHere) with breakpoints inserted accordingly.

This can be used to catch both types of NaN as well as other odd values.

It is up to you to determine the appropriate action to take. You can make a function call out of this if you have many such places to test.

Cleaned up code follows

JFP_CLASS = FP_CLASS(YourVariableHere)
SELECT CASE (JFP_CLASS)
CASE(0) ! FOR_K_FP_SNAN
YourVariableHere = 0
CASE(1) ! FOR_K_FP_QNAN
YourVariableHere = 0
CASE(2) ! FOR_K_FP_POS_INF
! do nothing
CASE(3) ! FOR_K_FP_NEG_INF
! do nothing
CASE(4) ! FOR_K_FP_POS_NORM
! do nothing
CASE(5) ! FOR_K_FP_NEG_NORM
! do nothing
CASE(6) ! FOR_K_FP_POS_DENORM
YourVariableHere = YourVariableHere ! try to normalize
CASE(7) ! FOR_K_FP_NEG_DENORM
YourVariableHere = YourVariableHere ! try to normalize
CASE(8) ! FOR_K_FP_POS_ZERO
! do nothing
CASE(9) ! FOR_K_FP_NEG_ZERO
YourVariableHere = 0
CASE DEFAULT
call DOSTOP('FOR_K_FP_unknown')
END SELECT

Jim Dempsey

I just tried that, but it still stops in the external routine with an exception on the line:

if(isnan(tables_pl(i))) then

The external routine is compiled with:

ifort /nologo /c /iface:cvf /Zi /nocheck /dbglibs /Tfmlf_restart_dynamic1.for /define:INTEL9 /Qsave /Fomlf_restart_dynamic1.obj /fpconstant /real_size:64 /Zi /4R8 /nocheck /extend-source:132 /debug-parameters:all

Adrian

Nope that still fails on the line:

JFP_CLASS = FP_CLASS(tables_pl(i))

when tables_pl(i) is a NaN
Same external routine is above, same compiler switches.

Jim,

Interesting observation: I have breakpoints set for cases 1 and 2 above. It stops for some NaN's in the correct place (case 1) and sets the value to zero, but for others it stops with the exception on the JFP_CLASS = FPCLASS(... line.

All NaN's have different bit patterns.

Adrian

Neither ISNAN nor FP_CLASS should trigger an error when a NaN is accessed. If you have an example where it does, please submit a test case to Intel Premier Support.

Steve - Intel Developer Support

It may be that you have an invalid address (as opposed to the contents at the address is NaN).

Your test was for an element of an array.

Check:

1) see that the array was allocated (or pointer valid)

2) if the array name is passed into a subroutine (which is where the failure is) make sure the dummy argument is indeed the address of thearray you assume it is.

3) verify the index is valid.

4) verify the array type is indeed what you expect it to be (e.g. the subroutine where the failure is encountered expects REAL(8) but the caller is passing in REAL(4), or INTEGER(4), ...)

5) check the other unusual JFP_CLASS case statements

Jim

> 1) see that the array was allocated (or pointer valid)

It is an array in a common block, and I can see its contents without any problemin the watch window.

> 2) if the array name is passed into a subroutine (which is where the failure is) make sure the dummy argument is indeed the address of thearray you assume it is.

n/a (common block var)

> 3) verify the index is valid.

index is 69019 (array is sized larger than that).

> 4) verify the array type is indeed what you expect it to be (e.g. the subroutine where the failure is encountered expects REAL(8) but the caller is passing in REAL(4), or INTEGER(4), ...)

It's a real*4 array.

> 5) check the other unusual JFP_CLASS case statements

The code works fine for the previous 69018 values of i.

See attached screen capture,

Adrian

Steve, I'm trying to reproduce this in a standalone program. I need to write out the bit patterns for these reals into some file, then read them in again into my standalone program. What format do I use write and read these bit patterns?

Adrian

Steve,

Never mind, I've reproduced it. I'll submit to Premier Support now.

Adrian

Is this a multi-threaded program?

Also, when you get the NaN set the display to Hex. This will help to determine if there is a bug in FP_CLASS. IEEE definition of NaN

Not A Number

The value NaN (Not a Number) is used to represent a value that does not represent a real number. NaN's are represented by a bit pattern with an exponent of all 1s and a non-zero fraction. There are two categories of NaN: QNaN (Quiet NaN) and SNaN (Signalling NaN).

A QNaN is a NaN with the most significant fraction bit set. QNaN's propagate freely through most arithmetic operations. These values pop out of an operation when the result is not mathematically defined.

An SNaN is a NaN with the most significant fraction bit clear. It is used to signal an exception when used in operations. SNaN's can be handy to assign to uninitialized variables to trap premature usage.

Semantically, QNaN's denote indeterminate operations, while SNaN's denote invalid operations.

Jim Dempsey

No, just a regular single threaded app. I have reproduced it on a small program and submitted it to premier support.

Adrian

FYI, the NaN causing this type of problem is:

data i1 /4287075194/
real*4 r1
equivalence (r1,i1)

if you're interested, I can send you the example.

Thanks for the statements. On my system the isnan test works as it should. See .JPG

Jim Dempsey

You have to compile the file with /Od /fpe:0 to see the problem - Premier support have reproduced it and are fixing it.

Adrian

First example (prior .JPG) had Floating-Point Exception Handling set to:

Produce NaN, signed infinities, and denormal results

This .JPG has Floating-Point Exception Handling set to:

Underflow gives 0.0; Abort on other IEE exceptions

Jim Dempsey

If you change the first access of the NaN to an FP_CLASS statement and compile with /Od /fpe:0, you will get the error I mention. Here is my code:

program nantest
implicit none
integer*4 i1
integer JFP_CLASS
data i1 /4287075194/
real*4 r1
equivalence (r1,i1)
JFP_CLASS = FP_CLASS(r1)
end

Adrian

Adrian,

Here is a work around. Note the isnan works when exceptions enabled.

Jim Dempsey

Jim,

That works, thanks. From what I interpret of the fixNaN function, itwill catchall real numbers where the 1st (or is it last?) byte is FF. While that will catch all NaN's as per your previous definition, will it not catch other non-NaN's as well - ie. can a regular number not have an FF in this position?

Adrian

Jim,

Here's another NaNbut it failsyour fixNaN check (2141473776 - right most byte is 7F)

Adrian

Jim,

Well I guess I'm confused now: I found good reals (notNaN's) with both FF and 7F as the most significant byte. These are FF000000 (-1.7014118E+38) and 7F000000 (+1.7014118E+38).

The former would fail your fixNaN test, and the latter gives an example where you can't rely on 7F to give always a NaN either.

Adrian

Jim,

I got the answer through comp.lang.fortran. This works:

function fixNaN(i)
logical :: fixNaN
integer :: i
if((i .ge. Z'FF800001' .and. i .le. Z'FFFFFFFF') .or.
& (i .ge. Z'7F800001' .and. i .le. Z'7FFFFFFF')) then
fixNaN = .true.
else
fixNaN = .false.
endif
end function fixNaN

FYI, the standard is IEE 754, and the url is http://babbage.cs.qc.edu/courses/cs341/IEEE-754references.html

Adrian

Here is a more complete function (not debugged)

Code:

! test for NaN on REAL*4 (referenced as INTEGER*4
function fixNaN(i)
    logical :: fixNaN
    integer(4) :: i
    integer :: SignBit, ExponentBits, FractionBits, MSBFractionBit
    logical :: isNegative
    logical :: isSNaN   ! Signaling NaN
    logical :: isQNan   ! Quite (non) Signaling NaN
    logical :: isPlusZero
    logical :: isPositiveDenormalizedeReal
    logical :: isPositiveNormalizedeReal
    logical :: isPlusInfinity
    logical :: isPositiveSNaN   ! Signaling NaN
    logical :: isPositiveQNan   ! Quite (non) Signaling NaN
    logical :: isNegativeZero
    logical :: isNegativeDenormalizedeReal
    logical :: isNegativeNormalizedeReal
    logical :: isNegativeInfinity
    logical :: isNegativeSNaN   ! Signaling NaN
    logical :: isNegativeQNan   ! Quite (non) Signaling NaN

    fixNaN = .false.    ! defaults are false
    isNegative = .false.
    isSNaN = .false.
    isQNaN = .false.
    isPlusZero = .false.
    isPositiveDenormalizedeReal = .false.
    isPositiveNormalizedeReal = .false.
    isPlusInfinity = .false.
    isPositiveSNaN = .false.
    isPositiveQNaV = .false.
    isNegativeZero = .false.
    isNegativeDenormalizedeReal = .false.
    isNegativeNormalizedeReal = .false.
    isNegativeInfinity = .false.
    isNegativeSNaN = .false.
    isNegativeQNaN = .false.
    ! Bitfields in i
    ! Z'80000000' - Sign bit
    ! Z'7F800000' - Exponent
    ! Z'007FFFFF' - Fraction
    ! Z'00400000' - MSB of Fraction
    SignBit = IAND(i,Z'80000000')
    ExponentBits = IAND(i,Z'7F800000')
    FractionBits = IAND(i,Z'007FFFFF')
    MSBFractionBit = IAND(i,Z'00400000')
    if(SignBit .eq. 0) then
        if(ExponentBits .eq. 0) then
            if(FractionBits .eq. 0) then
                isPlusZero = .true.
                return
            endif
            isPositiveDenormalizedeReal = .true.
            return
        endif
        ! ExponentBits .ne. 0
        if(ExponentBits .eq. Z'7F800000') then
            if(FractionBits .eq. 0) then
                isPlusInfinity = .true.
                return
            endif
            if(MSBFractionBit .eq. 0) then
                isPositiveSNaN = .true.
                isSNaN = .true.
                fixNaN = .true.
                return
            endif
            isPositiveQNaN = .true.
            isQNaN = .true.
            fixNaN = .true.
            return
        endif
        isPositiveNormalizedeReal =  = .true.
        return
    endif
    ! SignBit .ne. 0
    if(ExponentBits .eq. 0) then
        if(FractionBits .eq. 0) then
            isNegativeZero = .true.
            return
        endif
        isNegativeDenormalizedeReal = .true.
        return
    endif
    ! ExponentBits .ne. 0
    if(ExponentBits .eq. Z'7F800000') then
        if(FractionBits .eq. 0) then
            isNegativeInfinity = .true.
            return
        endif
        if(MSBFractionBit .eq. 0) then
            isNegativeSNaN = .true.
            isSNaN = .true.
            fixNaN = .true.
            return
        endif
        isNegativeQNaN = .true.
        isQNaN = .true.
        fixNaN = .true.
        return
    endif
    isNegativeNormalizedeReal =  = .true.
    return
end function fixNaN

Leave a Comment

Please sign in to add a comment. Not a member? Join today