Community
cancel
Showing results for
Did you mean:
Black Belt
67 Views

## Array Notations - Divide by zero

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];
{
//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
7 Replies
Moderator
67 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

Valued Contributor II
67 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
Employee
67 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
Valued Contributor II
67 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;
};
...

Valued Contributor II
67 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
Employee
67 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
Valued Contributor II
67 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]