Intel® C++ Compiler
Community support and assistance for creating C++ code that runs on platforms based on Intel® processors.

Array Notations - Divide by zero

jimdempseyatthecove
Honored Contributor III
443 Views
Is there a way to ex-post-facto correctly program divide by zero situations for array notations?

A[0:N] = B[0:N] / C[0:N];
if(fnHadDivideByZero())
{
//fix result
...
}

I know that I can use __sec_reduce_all_nonzero(C[0:N]) to verify prior to the divide that the divisor has no zeros.

if(__sec_reduce_all_nonzero(C[0:N]))
{
A[0:N] = B[0:N] / C[0:N];
} else {
// alternate path
}


However this adds overhead on every pass through that section of code.
The test should contain a mask for the usual errors ( divide by 0.0, NaN, Infinity, overflow, ...)

Has anyone given this some thought?

Jim Dempsey
0 Kudos
7 Replies
JenniferJ
Moderator
443 Views

I thought I responded, but apparently not.

We did discuss it internally: the intent with array notation is that there is no sequencing, i.e. the divides can occur in any order, so we cannot say which caused a divide by zero.


Jennifer

0 Kudos
SergeyKostrov
Valued Contributor II
443 Views
Is there a way to ex-post-facto correctly program divide by zero situations for array notations?
...
However this adds overhead on every pass through that section of code.

The test should contain a mask for the usual errors ( divide by 0.0, NaN, Infinity, overflow, ...)

Has anyone given this some thought?

I had a similar issue and in case of vectorsan extra pass is always done to verify that there are no any Zero values.

Another solution could be based on SEH forWindows platforms.I think somethinglike SEHis applicableon Linux.

Best regards,
Sergey
0 Kudos
Georg_Z_Intel
Employee
443 Views
Hello Jim,

there's a way that also works well with Intel Cilk Plus array notation extensions and vectorization:
FP exceptions

See this trivial example for Linux* (Windows* should be similar):

[cpp]#include #include #include #include using namespace std; void sigfpe(int x) { cout << "There was a SIGFPE signal!" << endl; exit(1); } #define N 10000 int main(int argc, char **argv) { double A, B, C; for (int i = 0; i < N; i++) { A = B = i + 1; C = 0; } B[100] = 0.0; // the guilty one signal(SIGFPE, sigfpe); feenableexcept(FE_DIVBYZERO); C[:] = A[:] / B[:]; fedisableexcept(FE_DIVBYZERO); return (C[0] == 0.0) ? 0 : 1; // avoid compiler being too clever for optimization }[/cpp]
Of course more code would be needed to seriously handle the division by zero. However, I assume that those exceptions should be very rare in normal operation and hence exception handling like this would be acceptable.

Best regards,

Georg Zitzlsberger
0 Kudos
SergeyKostrov
Valued Contributor II
443 Views
Is there a way to ex-post-facto correctly program divide by zero situations for array notations?
...
However this adds overhead on every pass through that section of code.

The test should contain a mask for the usual errors ( divide by 0.0, NaN, Infinity, overflow, ...)

Has anyone given this some thought?

I had a similar issue and in case of vectorsan extra pass is always done to verify that there are no any Zero values...


Divide by Zero detection in a C++ operator '/':

...
inline TDataSet< T, iDataType > & operator/( TDataSet< T, iDataType > &rtDs )
{
...
for( i = 0; i < m_uiN4; i += 4 )
{
if( ptData[i ] == ( T )0 || ptData[i+1] == ( T )0 ||
ptData[i+2] == ( T )0 || ptData[i+3] == ( T )0 )
{
return ( TDataSet< T, iDataType > & )*this;
}
}
...
// some processing
...
return ( TDataSet< T, iDataType > & )*this;
};
...

0 Kudos
SergeyKostrov
Valued Contributor II
443 Views
...
Of course more code would be needed to seriously handle the division by zero. However, I assume that those exceptions should be very rare in normal operation and hence exception handling like this would be acceptable...

>>void sigfpe( int x )
>>{
>> ...
>> exit(1);
>>}

In my case an application should continue processing. So, some kind of recovery has to be done as soon as a
'Divide By Zero' case(s) detected. Another issue is portability and a 'signal' based solution doesn't work for all
supported platforms and C++ compilers, unfortunately.

Best regards,
Sergey
0 Kudos
Georg_Z_Intel
Employee
443 Views
Hello Sergey,

of course! That was just an example.
The basic FP rounding & exception handling is specified by C99, so platform independent. However, the way the exceptions/signals show up is platform dependent. Anyways, every major platform has a way to configure that.

And, compared to what you proposed above this approach does not incur overhead by checking the operands (values) before computation!

That's the generated loop with the packed (SSE) divisions:

[bash]..B1.6: # Preds ..B1.4 ..B1.6 movaps 160128(%esp,%ebx,8), %xmm0 #26.5 movaps 160144(%esp,%ebx,8), %xmm1 #26.5 movaps 160160(%esp,%ebx,8), %xmm2 #26.5 movaps 160176(%esp,%ebx,8), %xmm3 #26.5 divpd 80128(%esp,%ebx,8), %xmm0 #26.19 FP exception here divpd 80144(%esp,%ebx,8), %xmm1 #26.19 ... or here divpd 80160(%esp,%ebx,8), %xmm2 #26.19 ... or here divpd 80176(%esp,%ebx,8), %xmm3 #26.19 ... or here movaps %xmm0, 128(%esp,%ebx,8) #26.5 movaps %xmm1, 144(%esp,%ebx,8) #26.5 movaps %xmm2, 160(%esp,%ebx,8) #26.5 movaps %xmm3, 176(%esp,%ebx,8) #26.5 addl $8, %ebx #26.5 cmpl $10000, %ebx #26.5 jb ..B1.6 # Prob 99% #26.5[/bash] (Similar for AVX)

As you see, no testing/checking of the operands! That's very fast and only requires the FP exception handler to be executed if there actually was an exception.

Best regards,

Georg Zitzlsberger
0 Kudos
SergeyKostrov
Valued Contributor II
443 Views
Hi everybody,

Please take a look at a test-case for aCRT function 'signal'. I've implemented it some time ago but decided to follow a simple
"pre-scan" solution to detect all zeros in a data set before a "divide-by-something" processing.
[cpp]... jmp_buf jmpbuf; // Address for long jump to jump to RTint g_iSignalCode = 0; // Signal Code RTint g_iSignalExtCode = 0; // Signal Extended Code RTvoid RTcdecl SignalHandler( PRTint iSignalCode, PRTint iSignalExtCode ); RTvoid RTcdecl SignalHandler( PRTint iSignalCode, PRTint iSignalExtCode ) { g_iSignalCode = iSignalCode; // Saves the Signal & Signal Extended Codes g_iSignalExtCode = iSignalExtCode; CrtReset87(); // Resets FPU CrtLongjmp( jmpbuf, -1 ); // Restores calling environment, } // returns to 'CrtSetjump' with a code -1 RTvoid SignalInfo( RTvoid ); RTvoid SignalInfo( RTvoid ) { CrtPrintf( RTU("Signal Code : %ldnSignal Extended Code: %ldn"), ( RTint )g_iSignalCode, ( RTint )g_iSignalExtCode ); CrtPrintf( RTU("Detected Floating-Point Error: ") ); switch( g_iSignalExtCode ) { case _RTFPE_INVALID: CrtPrintf( RTU("Invalid Numbern") ); break; case _RTFPE_ZERODIVIDE: CrtPrintf( RTU("Divide by Zeron") ); break; case _RTFPE_OVERFLOW: CrtPrintf( RTU("Overflown") ); break; case _RTFPE_UNDERFLOW: CrtPrintf( RTU("Underflown") ); break; case _RTFPE_INEXACT: CrtPrintf( RTU("Precision Lossn") ); break; default: CrtPrintf( RTU("Unknown or Unsupportedn") ); break; } } RTvoid RunTest1141( RTvoid ) { RTbool bOk = RTtrue; RTfloat fV1 = 777.0f; RTfloat fV2 = 0.0f; RTfloat fR = 0.0f; RTint jmpret = -1; #if ( defined ( _WIN32_BCC ) ) CrtControl87( _RTFPU_CW_DEFAULT, _RTFPU_MCW_EM ); #else CrtControl87( 1, _RTFPU_MCW_EM ); #endif if( CrtSignal( _RTSIGFPE, ( RTvoid ( RTcdecl * )( PRTint ) )SignalHandler ) == _RTSIGERR ) { bOk = RTfalse; CrtPrintf( RTU("Failed to Set a Handler for Detection of a Floating-Point Errorn") ); } CrtPrintf( RTU("Dividing %.1f by %.1fn"), fV1, fV2 ); if( bOk == RTtrue ) { jmpret = CrtSetjmp( jmpbuf ); if( jmpret == 0 ) { fR = fV1 / fV2; } else { SignalInfo(); } } } ... [/cpp]
0 Kudos
Reply