Skip to content

Commit 2101587

Browse files
committed
Added a floating point exception guard.
1 parent 67d4712 commit 2101587

File tree

5 files changed

+102
-107
lines changed

5 files changed

+102
-107
lines changed

src/system.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,13 @@
4141
*/
4242
struct UnwindState
4343
{
44-
std::size_t frames_to_skip;
44+
/// The number of frames left to skip.
45+
std::size_t framesToSkip;
46+
47+
/// A pointer to the current frame.
4548
void * * current;
49+
50+
/// A pointer to the final frame.
4651
void * * end;
4752
};
4853

@@ -57,9 +62,9 @@ static _Unwind_Reason_Code unwindCallback( _Unwind_Context * const context, void
5762
// Note: do not write `::_Unwind_GetIP` because it is a macro on some platforms.
5863
// Use `_Unwind_GetIP` instead!
5964
UnwindState * const state = static_cast< UnwindState * >(arg);
60-
if( state->frames_to_skip )
65+
if( state->framesToSkip )
6166
{
62-
--state->frames_to_skip;
67+
--state->framesToSkip;
6368
return _Unwind_GetIP( context ) ? _URC_NO_REASON : _URC_END_OF_STACK;
6469
}
6570

@@ -525,7 +530,9 @@ int enableFloatingPointExceptions( int const exceptions )
525530

526531
return fesetenv( &fenv ) ? -1 : oldExcepts;
527532
#else
528-
return feenableexcept( exceptions );
533+
int const oldExceptions = feenableexcept( exceptions );
534+
LVARRAY_ERROR_IF_EQ( oldExceptions, -1 );
535+
return oldExceptions;
529536
#endif
530537
}
531538

@@ -552,7 +559,9 @@ int disableFloatingPointExceptions( int const exceptions )
552559

553560
return fesetenv( &fenv ) ? -1 : oldExcepts;
554561
#else
555-
return fedisableexcept( exceptions );
562+
int const oldExceptions = fedisableexcept( exceptions );
563+
LVARRAY_ERROR_IF_EQ( oldExceptions, -1 );
564+
return oldExceptions;
556565
#endif
557566
}
558567

src/system.hpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,32 @@ int disableFloatingPointExceptions( int const exceptions );
103103
*/
104104
void setFPE();
105105

106+
/**
107+
* @class FloatingPointExceptionGuard
108+
* @brief Changes the floating point environment and reverts it when destoyed.
109+
*/
110+
class FloatingPointExceptionGuard
111+
{
112+
public:
113+
/**
114+
* @brief Disable the floating point exceptions given by @p exceptions.
115+
* @param exceptions The floating point exceptions to disable.
116+
*/
117+
FloatingPointExceptionGuard( int const exceptions ):
118+
m_previousExceptions( disableFloatingPointExceptions( exceptions ) )
119+
{}
120+
121+
/**
122+
* @brief Re-enable the floating point exceptions that were active on construction.
123+
*/
124+
~FloatingPointExceptionGuard()
125+
{ enableFloatingPointExceptions( m_previousExceptions ); }
126+
127+
private:
128+
/// The floating point exceptions that were active on construction.
129+
int const m_previousExceptions;
130+
};
131+
106132
/**
107133
* @return A string representing @p bytes converted to either
108134
* KB, MB, or GB.

unitTests/testFloatingPointExceptions.cpp

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,35 +25,61 @@ using namespace testFloatingPointExceptionsHelpers;
2525

2626
const char IGNORE_OUTPUT[] = ".*";
2727

28-
TEST( TestFloatingPointEnvironment, test_FE_UNDERFLOW_flush )
28+
namespace LvArray
2929
{
30-
LvArray::system::enableFloatingPointExceptions( FE_UNDERFLOW );
31-
EXPECT_DEATH_IF_SUPPORTED( uf_test( DBL_MIN, 2 ), IGNORE_OUTPUT );
32-
LvArray::system::disableFloatingPointExceptions( FE_UNDERFLOW );
30+
namespace testing
31+
{
32+
33+
TEST( TestFloatingPointEnvironment, Underflow )
34+
{
35+
system::enableFloatingPointExceptions( FE_UNDERFLOW );
36+
EXPECT_DEATH_IF_SUPPORTED( divide( DBL_MIN, 2 ), IGNORE_OUTPUT );
37+
system::disableFloatingPointExceptions( FE_UNDERFLOW );
3338

34-
LvArray::system::setFPE();
35-
double fpnum = uf_test( DBL_MIN, 2 );
39+
system::setFPE();
40+
double fpnum = divide( DBL_MIN, 2 );
3641
int fpclassification = std::fpclassify( fpnum );
3742
EXPECT_NE( fpclassification, FP_SUBNORMAL );
3843
}
3944

40-
TEST( TestFloatingPointEnvironment, test_FE_DIVBYZERO )
45+
TEST( TestFloatingPointEnvironment, DivideByZero )
4146
{
42-
LvArray::system::setFPE();
43-
EXPECT_DEATH_IF_SUPPORTED( func3( 0.0 ), IGNORE_OUTPUT );
47+
system::setFPE();
48+
EXPECT_DEATH_IF_SUPPORTED( divide( 1, 0 ), IGNORE_OUTPUT );
4449
}
4550

51+
TEST( TestFloatingPointEnvironment, Overlow )
52+
{
53+
system::setFPE();
54+
EXPECT_DEATH_IF_SUPPORTED( multiply( DBL_MAX, 2 ), IGNORE_OUTPUT );
55+
}
4656

47-
TEST( TestFloatingPointEnvironment, test_FE_OVERFLOW )
57+
TEST( TestFloatingPointEnvironment, Invalid )
4858
{
49-
LvArray::system::setFPE();
50-
EXPECT_DEATH_IF_SUPPORTED( of_test( 2, DBL_MAX ), IGNORE_OUTPUT );
59+
system::setFPE();
60+
EXPECT_DEATH_IF_SUPPORTED( invalid(), IGNORE_OUTPUT );
5161
}
5262

53-
TEST( TestFloatingPointEnvironment, test_FE_INVALID )
63+
TEST( TestFloatingPointEnvironment, FloatingPointExceptionGuard )
5464
{
55-
LvArray::system::setFPE();
56-
EXPECT_DEATH_IF_SUPPORTED( invalid_test( 0.0 ), IGNORE_OUTPUT );
65+
system::setFPE();
66+
67+
{
68+
system::FloatingPointExceptionGuard guard( FE_UNDERFLOW );
69+
divide( DBL_MIN, 2 );
70+
EXPECT_DEATH_IF_SUPPORTED( multiply( DBL_MAX, 2 ), IGNORE_OUTPUT );
71+
}
5772
}
5873

74+
} // namespace testing
75+
} // namespace LvArray
76+
5977
#endif
78+
79+
// This is the default gtest main method. It is included for ease of debugging.
80+
int main( int argc, char * * argv )
81+
{
82+
::testing::InitGoogleTest( &argc, argv );
83+
int const result = RUN_ALL_TESTS();
84+
return result;
85+
}

unitTests/testFloatingPointExceptionsHelpers.cpp

Lines changed: 14 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,79 +6,30 @@
66
*/
77

88

9-
#include <iostream>
109
#include "Macros.hpp"
1110
#include "system.hpp"
1211
#include "system.hpp"
1312
#include "testFloatingPointExceptionsHelpers.hpp"
1413

15-
namespace testFloatingPointExceptionsHelpers
16-
{
17-
18-
void func3( double divisor )
19-
{
20-
double a = 1.0 / divisor;
21-
std::cout << "1.0/0.0 didn't kill program, result is " << a;
22-
}
23-
24-
void func2( double divisor )
25-
{
26-
func3( divisor );
27-
}
28-
29-
void func1( double divisor )
30-
{
31-
func2( divisor );
32-
}
33-
34-
void func0( double divisor )
35-
{
36-
func1( divisor );
37-
}
38-
39-
void testStackTrace( double divisor )
40-
{
41-
LvArray::system::setSignalHandling( []( int const signal ) { LvArray::system::stackTraceHandler( signal, true ); } );
42-
func0( divisor );
43-
}
44-
45-
//TEST(testStackTrace_DeathTest, stackTrace)
46-
//{
47-
// EXPECT_DEATH_IF_SUPPORTED(testStackTrace(0), IGNORE_OUTPUT);
48-
//}
49-
14+
#include <fenv.h>
15+
#include <cmath>
16+
#include <float.h>
5017

18+
#if defined(__x86_64__)
19+
#include <xmmintrin.h>
20+
#include <iostream>
21+
#endif
5122

52-
void show_fe_exceptions( void )
23+
namespace testFloatingPointExceptionsHelpers
5324
{
54-
printf( "exceptions raised:" );
55-
if( fetestexcept( FE_DIVBYZERO ))
56-
printf( " FE_DIVBYZERO" );
57-
if( fetestexcept( FE_INEXACT ))
58-
printf( " FE_INEXACT" );
59-
if( fetestexcept( FE_INVALID ))
60-
printf( " FE_INVALID" );
61-
if( fetestexcept( FE_OVERFLOW ))
62-
printf( " FE_OVERFLOW" );
63-
if( fetestexcept( FE_UNDERFLOW ))
64-
printf( " FE_UNDERFLOW" );
65-
feclearexcept( FE_ALL_EXCEPT );
66-
printf( "\n" );
67-
}
6825

69-
double uf_test( double x, double denominator )
70-
{
71-
return x / denominator;
72-
}
26+
double multiply( double const x, double const y )
27+
{ return x * y; }
7328

74-
double of_test( double x, double y )
75-
{
76-
return x * y;
77-
}
29+
double divide( double const x, double const y )
30+
{ return x / y; }
7831

79-
double invalid_test( double LVARRAY_UNUSED_ARG( x ) )
80-
{
81-
return std::acos( 2.0 );
82-
}
32+
double invalid()
33+
{ return std::acos( 2.0 ); }
8334

8435
}

unitTests/testFloatingPointExceptionsHelpers.hpp

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,16 @@
55
* SPDX-License-Identifier: (BSD-3-Clause)
66
*/
77

8-
9-
10-
#include <fenv.h>
11-
#include <cmath>
12-
#include <float.h>
13-
14-
#if defined(__x86_64__)
15-
#include <xmmintrin.h>
16-
#endif
17-
188
namespace testFloatingPointExceptionsHelpers
199
{
20-
void func3( double divisor );
21-
22-
void func2( double divisor );
23-
24-
void func1( double divisor );
25-
26-
void func0( double divisor );
27-
28-
void testStackTrace( double divisor );
2910

30-
void show_fe_exceptions( void );
11+
/// Return @p x / @p y.
12+
double divide( double const x, double const y );
3113

32-
double uf_test( double x, double denominator );
14+
/// Return @p x * @p y.
15+
double multiply( double const x, double const y );
3316

34-
double of_test( double x, double y );
17+
/// Performs an invalid floating point operations (generate a NaN).
18+
double invalid();
3519

36-
double invalid_test( double x );
37-
}
20+
} // namespace testFloatingPointExceptionsHelpers

0 commit comments

Comments
 (0)