Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

fpe and trapping run time errors

groupw_bench
Beginner
4,293 Views

I'm using IVF v. 10.1.025 to compile for a 32 bit Windows system and running the compiled program under XP.

An IVF-compiled exe on a user's machine is generating a lot of NaN values which it shouldn't, and I'm not able to duplicate it on my machine. I'd like to create a debug version of the exe which would stop and report the first place a floating point overflow or other problem occurs. But I haven't been able to figure out how. I've tried compiling for debug with, among others, the following compiler options:

/traceback

/check: all

/fpe:0

/Zi

and the /DEBUG linker option.

It sounds from the description of the /fpe option that specifying /fpe:0 should cause an exception to occur when a floating point overflow occurs. But it doesn't. In a test program which does the operation 1./0. and assigns it to a floating point variable, no error occurs. A printout of the variable's value shows "Infinity". Assigning the variable's infinite value to an integer results in the integer having the value -2147483648. Dividing an integer variable equal to 1 by an integer variable equal to zero results in a Windows exception message "[program] has encountered a problem and needs to close. We are sorry for the inconvenience."

Is it possible to trap floating point overflows and other errors in an .exe file and get any meaningful information about where in the program it occurred? If so, how? The user won't, of course, have the compiler available.

Thanks!

0 Kudos
19 Replies
Xiaoping_D_Intel
Employee
4,293 Views

Can you post a test case? I tried to build one and gotdifferent result from yours:

>type test.f90

program main
real a, b, c

a = 0
b = 1
c = b / a
write(*,*) "c = ",c
end

>ifort test.f90

>test.exe

c = Infinity

>ifort /fpe:0 /traceback /check:all /Zi test.f90

>test.exe

forrtl: error (73): floating divide by zero
Image PC Routine Line Source
test.exe 00401079 _MAIN__ 6 test.f90
test.exe 004762AD Unknown Unknown Unknown
test.exe 0044C5A1 Unknown Unknown Unknown
kernel32.dll 7C816FD7 Unknown Unknown Unknown

0 Kudos
groupw_bench
Beginner
4,293 Views

Thanks for the response. Your test program works just as you said. However, mine isn't a console application but a "Windowing" application. Here's a test program that's more like mine:

INTEGER (KIND = 4) function WinMain(hInstance, hPrevInstance, lpszCmdLine, nCmdShow )

!DEC$ ATTRIBUTES STDCALL, ALIAS : '_WinMain@16' :: WinMain

INTEGER (KIND = 4) :: hInstance, hPrevInstance, lpszCmdLine, nCmdShow
real a, b, c

open (UNIT=1, FILE='Test.txt', STATUS='replace')

a = 0
b = 1
c = b / a
write(1,*) "c = ",c
a = 5
write(1,*) "a = ",a
WinMain = 0
end

I used the default settings for a debug build except for setting fpe:0. Here's what's printed in Text.txt after running the program:

c = Infinity
a = 5.000000

The fact that the new assignment for the value of a was made and printed shows that the divide by zero error didn't stop the program. I expect to see a window showing the same error tracking information that your program sends to the console. But no error message shows, and the program just keeps going after happily dividing by zero.

0 Kudos
William_H_Intel3
Employee
4,293 Views
Quoting - groupw_bench

Thanks for the response. Your test program works just as you said. However, mine isn't a console application but a "Windowing" application. Here's a test program that's more like mine:

INTEGER (KIND = 4) function WinMain(hInstance, hPrevInstance, lpszCmdLine, nCmdShow )

!DEC$ ATTRIBUTES STDCALL, ALIAS : '_WinMain@16' :: WinMain

INTEGER (KIND = 4) :: hInstance, hPrevInstance, lpszCmdLine, nCmdShow
real a, b, c

open (UNIT=1, FILE='Test.txt', STATUS='replace')

a = 0
b = 1
c = b / a
write(1,*) "c = ",c
a = 5
write(1,*) "a = ",a
WinMain = 0
end

I used the default settings for a debug build except for setting fpe:0. Here's what's printed in Text.txt after running the program:

c = Infinity
a = 5.000000

The fact that the new assignment for the value of a was made and printed shows that the divide by zero error didn't stop the program. I expect to see a window showing the same error tracking information that your program sends to the console. But no error message shows, and the program just keeps going after happily dividing by zero.

In a windows application, you have to set up the floating point control word yourself to enable the traps. This is described in the documentation. See the sections about handling floating point exceptions,impact of application type, etc.

Bill Hilliard

0 Kudos
groupw_bench
Beginner
4,293 Views
Quoting - bill_hilliard

In a windows application, you have to set up the floating point control word yourself to enable the traps. This is described in the documentation. See the sections about handling floating point exceptions,impact of application type, etc.

Bill Hilliard

Thanks. I added a CALL SETCONTROLFPQQ statement to the program, and now the divide by zero produces a general Windows fault (" has encountered a problem and needs to close. We are sorry for the inconvenience."). So that's a start -- at least an error is occurring now. But it sounds like I have to write my own error handler (in C, it looks like) in order to get the traceback information that the console program provides automatically. Or is there some reasonably simple way to do it? To reiterate, what I want is a way to detect the point in my program where the first NaN, Infinity, or other normally non-valid floating point result occurs, in an environment where the user doesn't have access to the source code. Is this possible without a huge amount of learning and effort?

0 Kudos
William_H_Intel3
Employee
4,293 Views
[cpp]__try {
[/cpp]
Quoting - groupw_bench

Thanks. I added a CALL SETCONTROLFPQQ statement to the program, and now the divide by zero produces a general Windows fault (" has encountered a problem and needs to close. We are sorry for the inconvenience."). So that's a start -- at least an error is occurring now. But it sounds like I have to write my own error handler (in C, it looks like) in order to get the traceback information that the console program provides automatically. Or is there some reasonably simple way to do it? To reiterate, what I want is a way to detect the point in my program where the first NaN, Infinity, or other normally non-valid floating point result occurs, in an environment where the user doesn't have access to the source code. Is this possible without a huge amount of learning and effort?

How hard it is depends on your comfort level with Windows structured exception handling I suppose. The idea is to wrap your Fortran code in a try/except block. In your example above, you would move the Fortran code into some other function, call it MyFortranCode() or whatever you like. Then write your WinMain() code in a C file. The WinMain() code just calls MyFortranCode() something like:

[cpp]__try {
  sts = MyFortranCode();
} __except(insert useful filter expression that invokes your handler)
{;}
[/cpp]

The filter expression needs to capture the exception information returned by the OS. Your handler could be a Fortran routine that just calls TRACEBACKQQ() and exits, for example. You would pass the exceptioninfo to tracebackqq().

That is the basic idea. I guess you willhave todecide if it is worth your time to figure out the details. Hope this helps some.

Bill

0 Kudos
Kevin_D_Intel
Employee
4,293 Views

I was able to adapt your initial Fortran WinMain example and code I received from a Fortran RTL developer recently based on another customer's interest that I believe closely matches yours, and fits with Bill's guidance too, so I thought I would share it.

The example creates a WinMain driver in C to call the Fortran function and leverages the Fortran RTLs to trap the floating-point divide and present the Fortran RTL symbolized traceback in a pop-up window (see attached .bmp).

I hope it is useful.

Compile the code as follows:

ifort -c -Od -Zi /fpe:0 /traceback formain.f90

cl -c -Od -Zi MyWinMain.c

ifort /exe:myFPE.exe formain.obj MyWinMain.obj /link /machine:i386 /subsystem:windows

MyWinMain.c:

============

[cpp]#include 
#include 
#include 
#include 

#define FPE_M_TRAP_DIV0 0x00000004

static LPEXCEPTION_POINTERS qwin_eptr;
static int qwin_fpe_ieee_sts;

#if defined(__cplusplus)    /* C linkage */
extern "C" {
#endif
extern int for_set_fpe_ (unsigned int *);
extern int for_rtl_init_ (void); /* RTL initialization routine */
extern int for_rtl_finish_ (void); /* RTL Cleanup routine */
extern int for__nt_signal_handler( _FPIEEE_RECORD *pieee,
                                   LPEXCEPTION_POINTERS eptr,
                                   int fpe_ieee_sts,
                                   int  *code_ptr,
                                   void (__cdecl **user_handler_ptr)(int) );

#if defined(__cplusplus)    /* end C linkage */
}
#endif

int qwin__nt_handler_jacket ( _FPIEEE_RECORD *pieee )
{
  return ( for__nt_signal_handler( pieee, qwin_eptr, qwin_fpe_ieee_sts, NULL, NULL ) );
}

/*------------------------------------------*
 |                 WinMain                  |
 *------------------------------------------*/
int APIENTRY WinMain (HINSTANCE hInstance,   HINSTANCE hPrevInstance,
                      LPSTR lpszCmdLine, int nCmdShow)
{
   int  nReturn;
   unsigned int trap_mask ;

   __try
   {
     __try
     {
       for_rtl_init_();
       trap_mask = FPE_M_TRAP_DIV0;
       trap_mask = for_set_fpe_ (&trap_mask);

       nReturn = FORMAIN(hInstance,   hPrevInstance, lpszCmdLine, nCmdShow);

     }
     
     __except (
               (qwin_fpe_ieee_sts = _fpieee_flt ( GetExceptionCode(),
                qwin_eptr = GetExceptionInformation(),
                qwin__nt_handler_jacket ) ) ==
                EXCEPTION_CONTINUE_SEARCH ?
                qwin__nt_handler_jacket( NULL ) : qwin_fpe_ieee_sts
              )
              
              {;}  /* Just leave this null for now. */
    }       /* End of try/except block */
    __finally
    {
      /*
       ** Do any clean up required.
       */
      for_rtl_finish_();
    }

   nReturn = 0;
   exit (nReturn);

}  /* WinMain */
[/cpp]

formain.f90:

============

[cpp]INTEGER (KIND = 4) function ForMain(hInstance, hPrevInstance, lpszCmdLine, 
nCmdShow )

INTEGER (KIND = 4) :: hInstance, hPrevInstance, lpszCmdLine, nCmdShow
real a, b, c

open (UNIT=1, FILE='Test.txt', STATUS='replace')

a = 0
b = 1
c = b / a

write(1,*) "c = ",c
a = 5
write(1,*) "a = ",a

ForMain = 0
end
[/cpp]
0 Kudos
groupw_bench
Beginner
4,293 Views

I want to thank you very much for taking the time and trouble to put this together. However, the program I really need this for would almost certainly require a good deal more work to adapt, since it's composed of a large number of files and modules, and it's taken quite a while for me to determine what options I need to use, which files I need to include, and so forth. I haven't done any C programming for quite a long time, so I'm also apprehensive that I'd have a fair amount of fiddling to do in getting the compiler up and running and happy with the code. I was looking for a simple solution, but see that there isn't any. Although it'll take quite a while to make up diagnostic programs to try and find where the NaN results first happen, I'm pretty sure it'll be faster overall than pursuing the dual language route.

Thanks again to everyone who responded.

0 Kudos
g_f_thomas
Beginner
4,293 Views

For fpe's, the all Fortran solution would be via theIEEE EXCEPTION HANDLING feature of F2003. IVF doesn't support it yet. FWIT, lodge acomplaint (not a request, it's long overdue) with Premier Support.

BTW, don't assume that the posted workaround actually works under all circumstances likely to be encountered on Windows by the average Joe programmer.

Gerry

0 Kudos
Steven_L_Intel1
Employee
4,293 Views

The IEEE modules are in v11 - coming REAL SOON to a PC near you.

0 Kudos
g_f_thomas
Beginner
4,293 Views

The IEEE modules are in v11 - coming REAL SOON to a PC near you.

That's great. Horrah for v11.

Gerry

0 Kudos
crkkos
Beginner
4,293 Views

i have an old unix code that uses FFLAGS -K, -C +u77 +fp_exception +01

and LDFLAGS = +u77 -W1 -a, archive +FPVZO

im porting the code over to linux, and when i use the fpe0 flag, the program crashes.

i look in the code and there is a variable X = A/B. B happens to be 0, which is why of course the program

crashes. In the old code I am able to get results that have X = 0, but in the new code using less scrict fpe flags

the number is NAN. Is there a way to get the output to run and compile and set NAN values to 0? Some kinda loader flag or option?

0 Kudos
Steven_L_Intel1
Employee
4,293 Views

There is no option to set the result of a zerodivide to zero. While you could use the IEEE modules to do the computation, test for the error and then change the value, the simpler way is to just test for the divisor being zero before doing the divide.

0 Kudos
crkkos
Beginner
4,293 Views
yeah in the code i changed it to say if B = 0, then B = 1, so it wouldnt give me the NAN, and this worked, which i expected, i was just wondering if there was a Fortran flag option or loader flag option to do this so i didnt haev to change the code. Do you happen to know which flag, or the reason why the orignal version set the value to 0.00 and not NAN?

0 Kudos
Steven_L_Intel1
Employee
4,293 Views

There is no option or flag. I'm not aware that our compiler has ever offered such a thing - perhaps some other compiler on another platform did.

0 Kudos
dannycat
New Contributor I
4,293 Views

Steve,

In an earlier reply to this thread you said that the fpe feature of FORTRAN 2003 would be available soon in version 11.??. Are we talking days, weeks or months?

0 Kudos
Steven_L_Intel1
Employee
4,293 Views

How about negative months? The intrinsic modules IEEE_ARITHMETIC, IEEE_EXCEPTIONS and IEEE_FEATURES are in version 11.0, available for a month and a half now.

0 Kudos
dannycat
New Contributor I
4,293 Views

How about negative months? The intrinsic modules IEEE_ARITHMETIC, IEEE_EXCEPTIONS and IEEE_FEATURES are in version 11.0, available for a month and a half now.


Thanks Steve,

I was deceived by COMING SOON which implies it's not yet available. I do have version 11 so I'll have a look in the release notes and at any samples that are included.

0 Kudos
Steven_L_Intel1
Employee
4,293 Views

I wrote that on November 3. Version 11 was released the next day. I think.

0 Kudos
Steven_L_Intel1
Employee
4,293 Views

Here's an example from the standard on using the IEEE FP support.

[cpp]USE, INTRINSIC :: IEEE_EXCEPTIONS
USE, INTRINSIC :: IEEE_FEATURES, ONLY: IEEE_INVALID_FLAG
! The other exceptions of IEEE_USUAL (IEEE_OVERFLOW and
! IEEE_DIVIDE_BY_ZERO) are always available with IEEE_EXCEPTIONS
TYPE(IEEE_STATUS_TYPE) STATUS_VALUE
LOGICAL, DIMENSION(3) :: FLAG_VALUE
...
CALL IEEE_GET_STATUS(STATUS_VALUE)
CALL IEEE_SET_HALTING_MODE(IEEE_USUAL,.FALSE.) ! Needed in case the
! default on the processor is to halt on exceptions
CALL IEEE_SET_FLAG(IEEE_USUAL,.FALSE.)
! First try the "fast" algorithm for inverting a matrix:
MATRIX1 = FAST_INV(MATRIX) ! This shall not alter MATRIX.
CALL IEEE_GET_FLAG(IEEE_USUAL,FLAG_VALUE)
IF (ANY(FLAG_VALUE)) THEN
! "Fast" algorithm failed; try "slow" one:
  CALL IEEE_SET_FLAG(IEEE_USUAL,.FALSE.)
  MATRIX1 = SLOW_INV(MATRIX)
  CALL IEEE_GET_FLAG(IEEE_USUAL,FLAG_VALUE)
  IF (ANY(FLAG_VALUE)) THEN
    WRITE (*, *) Cannot invert matrix
    STOP
    END IF
  END IF
CALL IEEE_SET_STATUS(STATUS_VALUE)
[/cpp]

0 Kudos
Reply