Floating Point Exceptions - openmpp/openmpp.github.io GitHub Wiki
Home > Model Development Topics > Floating Point Exceptions
This topic describes how to activate hardware floating point exceptions to identify model bugs using the fp_exceptions
option.
- Introduction Non-numeric values, exceptions, purpose, limitations
- Activation Syntax
- Use Approaches to identifying model logic errors using floating point exceptions
- Debug mode example Illustration with screenshot of trapped exception
- Release mode examples Model logs with no exception trapping
The C++ standard treats the result of a floating-point operation which has no numeric result, like division by zero, using special
non-numeric floating-point values such as
+inf
, -inf
, and NaN
(“not a number”).
OpenM++ follows the C++ standard,
and
actively uses non-numeric values
in several contexts.
C++ model code can also produce non-numeric values. Sometimes this is intended, for example to populate a cell of a derived table with a non-numeric value when the cell value is undefined by the model design and so create an empty cell with no numeric value. Other times a non-numeric result is not intended, and C++ model code produces a non-numeric value due to a model logic error. The C++ standard does not consider that to be a runtime error, and execution flow will continue uninterrupted, using the non-numeric result of the operation. That can hide the model logic error, by eliminating obvious symptoms when the model is run and in model outputs. In an experiment with a large complex model, multiple model logic errors were hidden very effectively in exactly this way.
Floating point hardware can be configured to deviate from normal C++ standard behaviour, and instead treat a mathematical operation which produces a non-numeric value as a runtime error by "raising a floating point exception". A floating point exception is normally a fatal condition which causes the running program to immediately crash (aka "fault"). If the program is running in Debug mode in an IDE like Visual Studio, floating point exceptions can instead be "trapped" and execution halted without program exit, which preserves the complete state of execution (function calls, values of variables) when the floating point exception was raised. That reveals the exact model code location responsible for the invalid mathematical operation, as well as the values of relevant variables and function calls, to help model developers understand the underlying logic error in model code. See Debug mode example for an example.
The fp_exceptions
option deliberately activates floating point hardware exceptions to reveal hidden model logic errors.
It should be used with caution, because it breaks standard C++ behaviour and deliberately causes a model to crash at runtime when a floating point exception is raised.
It is intended to help model developers identify model logic errors during model development and testing.
So, fp_exceptions = on
causes the model to write a warning message to the run log every time the model is run, to make clear that hardware floating point exceptions are enabled and that the model might crash as a result.
This warning message is configurable and can be disabled, as described
below.
Because OpenM++ is cross-platform by design, it deliberately avoids adding platform-specific and hardware-specific functionality.
The fp_exceptions
option is an exception to this design principle, justified by its utility in identifying model logic errors.
Necessarily, the internal implementation of fp_exceptions
differs by platform and hardware, and it is not supported in all environments.
fp_exceptions
is supported for Windows (msvc) and Linux (gcc).
It is not currently supported for clang (MacOS).
The fp_exceptions
option is off
by default.
To activate floating point exceptions insert the following statement in model code, for example in the file MODEL/code/ompp_options.ompp
.
options fp_exceptions = on; // If on, raise runtime floating point exception on division by zero or overflow
When fp_exceptions = on
, the model will emit a warning to the run log on every run.
On Windows (msvc) and Linux (gcc), it writes the message
Warning : model will fault on division by zero with fp_exceptions = on
On MacOS (clang), it writes the message
Warning : option fp_exceptions not supported for clang (MacOS)
These runtime warning messages can be disabled using the fp_exceptions_warning
option,
whose default value is on
:
options fp_exceptions_warning = off; // If off, disable runtime floating point exception warning
If the model deliberately divides by zero in a few places, consider instead protecting those intentional uses by defensive code which tests the denominator explicitly, so that you can use fp_exceptions
to identify unintentional uses elsewhere in model code. Turning fp_exceptions
on and running in Debug mode is a straightforward way to find such intentional uses.
A floating point division by zero exception is thrown only if a non-zero number is divided by zero.
Zero divided by zero does not throw an exception. It silently returns NaN
and execution continues.
For models which do not deliberately divide by zero, consider leaving fp_exceptions
on in MODEL/ompp_options.ompp
for routine model development.
The option has little to no computational overhead and will proactively identify model logic issues as they arise during development, in both Debug mode and in Release mode.
If an exception is thrown in Release mode, consider executing the same run in Debug mode from the IDE (in Visual Studio Debug > Run (F5)
) to localize and understand the cause in model code. Configure the IDE to launch the model with command arguments which reproduce the Release run which faulted.
If model code is modified iteratively in minimal 'atomic' changes during model development (recommended), there may be few lines of model code modified since the previous iteration, and a logical examination of the incremental code changes may suffice to identify the cause (use git or visual indications in the IDE of modified code).
If a floating point exception is thrown in a Release version of a model and it is computationally infeasible to reproduce the same run in the IDE in Debug mode, and if an examination of model code changes proves unfruitful, identifying the code responsible for the fault is more difficult. Here are some notes and suggestions for that situation:
- A crash causes the program to exit immediately. That can truncate output files. Internally, output files are normally buffered for efficiency. That can mean that the final lines of an output file (such as a microdata
.csv
output file) may be missing. OpenM++ takes special measures to help assure that no lines are missing from the model run log if the model crashes. Consider inserting log output statements in suspected model code, or direct output to the console usingcout
, iterating progressively to isolate the responsible code by a binary search of model code. - Consider reverting model code changes progressively until a reversion is found which does not crash. The model code responsible for the crash is probably somewhere in the revision after that one. Next, progressively revert portions of that revision until the exception no longer occurs. The responsible model code is likely there.
- Include functionality parameter switches in the model, even if they don't make substantive sense. Turning them off progressively might help localize the faulty code somewhat.
- If the run is too large to reproduce the anomaly in Debug mode, consider performing multiple smaller Release runs with different random seeds to produce sufficient variation to trigger the fault in one of them. Then, repeat that smaller run in Debug mode in the IDE to determine the precise location of the fault. For example, if the fault occurs in a time-based model with a population of 1M, construct (perhaps with a helper script) 20 runs each with a population of 50K and different random seeds. Run those 50 in Release mode for speed, look for one which faults, and rerun that one in Debug mode in the IDE to determine the precise line of model code responsible. Because OpenM++ runs on multiple platforms and produces identical results on all platforms, consider doing the 50 smaller runs on a computational grid on a server instance in parallel for speed, then run the Debug version of a run which faults on a local development platform in the IDE.
This situation is best avoided if possible. Iterative model development with minimal 'atomic' code changes, and testing at each iteration using a small (or large) test run can be an effective defensive strategy. The test_models
utility was designed to facilitate such iterative development and testing. Each iteration of model development, perhaps only 10-20 lines of added or modified code, can be built, run, and compared against the previous iteration by issuing a one-line test_models
command. That adds model run and result comparison to each development cycle, rather than just model build.
This example uses a modified version of RiskPaths which deliberately divides by zero in a model event.
Model code activates floating point exceptions using fp_exceptions
.
The model was built in Debug mode and then run inside Visual Studio using Debug > Run
(F5
).
Model execution halted when the division by zero occurred, producing the screenshot below.
The screenshot shows execution suspended at the line of model code responsible for the division by zero, with a pop-up Exception unhandled
which describes the exception. The pop-up has translated the Windows-specific hexadecimal exception code 0xC000008E
into the human-readable Floating point division by zero
. The "parameters" in the pop-up contain no obviously useful information.
The call stack is visible in the lower pane.
Normal debugging functionality is available, e.g. examining variables by mouse hover or using the Watch windows, setting code execution to a previous line and stepping execution line-by-line before the fault, setting and hitting breakpoints, etc.
The same modified version of RiskPaths used in the Debug mode example was built in Release mode and run in several environments to illustrate what happens when the model faults when the hardware division by zero exception is raised. The model run log file and (sometimes) additional crash information provided by the run environment is shown for each example.
Example | OS | Environment | Notes |
---|---|---|---|
Example 1 | Windows | OpenM++ UI | Shows the exception return code after the final log entry. The run status of the job in the UI is shown as failed . The job status can be retrieved by scripts using oms . |
Example 2 | Windows | Command window | No indication that the program crashed. Return status is retrievable by scripts. |
Example 3 | Windows | Visual Studio | Shows the return code in the execution command window after the final log entry. |
Example 4 | Linux | Command shell | Shows a human-readable version of the return status after the final log entry. Return status is retrievable by scripts. |
Windows, OpenM++ UI:
2025-05-31 14:58:20.418[RiskPaths_extra.2025_05_31_14_58_20_325.console.log]
2025-05-31 14:58:20.389 RiskPaths_extra
2025-05-31 14:58:20.396 Model version : 3.0.0.0
2025-05-31 14:58:20.396 Model created : 2025-05-31 09:21:45.966
2025-05-31 14:58:20.396 Model digest : 652f1e74e727a20fc803ce928a75b3bc
2025-05-31 14:58:20.396 OpenM++ version: Development version (build from source code)
2025-05-31 14:58:20.396 OpenM++ build : Windows 64 bit Release
2025-05-31 14:58:20.396 OM_ROOT=C:/Development/X/ompp
2025-05-31 14:58:20.396 OM_RiskPaths_extra=C:\Development\X\ompp\models\RiskPaths_extra
2025-05-31 14:58:20.396 Model build : Windows 64 bit Release
2025-05-31 14:58:20.396 Prepare fixed and missing parameters
2025-05-31 14:58:20.396 Run: 2025_05_31_14_58_20_325
2025-05-31 14:58:20.412 Run: 103 RiskPaths_extra_Default_2025_05_31_14_58_09_483
2025-05-31 14:58:20.412 Warning : model can expose microdata at run-time with output_microdata = on
2025-05-31 14:58:20.412 Warning : model will fault on division by zero with fp_exceptions = on
2025-05-31 14:58:20.412 Get scenario parameters for process
2025-05-31 14:58:20.414 member=0 Bind scenario parameters
2025-05-31 14:58:20.414 member=0 Predicted memory required = 0 MB per parallel sub and 0 MB per instance
2025-05-31 14:58:20.414 member=0 Compute derived parameters
2025-05-31 14:58:20.414 member=0 Prepare for simulation
2025-05-31 14:58:20.414 member=0 Simulation progress=0% cases=0
2025-05-31 14:58:20.414 Welcome to the RiskPaths model!
exit status 0xc000008e
[back to Release mode examples]
[back to topic contents]
Windows, command window:
C:\Development\X\ompp\models\RiskPaths_extra\ompp\bin>RiskPaths_extra
2025-05-30 22:13:22.677 RiskPaths_extra
2025-05-30 22:13:22.684 Model version : 3.0.0.0
2025-05-30 22:13:22.686 Model created : 2025-05-30 21:41:32.533
2025-05-30 22:13:22.687 Model digest : 652f1e74e727a20fc803ce928a75b3bc
2025-05-30 22:13:22.689 OpenM++ version: Development version (build from source code)
2025-05-30 22:13:22.690 OpenM++ build : Windows 64 bit Release
2025-05-30 22:13:22.691 OM_ROOT=C:/Development/X/ompp
2025-05-30 22:13:22.692 Model build : Windows 64 bit Release
2025-05-30 22:13:22.693 Prepare fixed and missing parameters
2025-05-30 22:13:22.694 Run: 2025_05_30_22_13_22_632
2025-05-30 22:13:22.708 Run: 104 RiskPaths_extra_Default
2025-05-30 22:13:22.710 Warning : model can expose microdata at run-time with output_microdata = on
2025-05-30 22:13:22.711 Warning : model will fault on division by zero with fp_exceptions = on
2025-05-30 22:13:22.712 Get scenario parameters for process
2025-05-30 22:13:22.714 member=0 Bind scenario parameters
2025-05-30 22:13:22.716 member=0 Predicted memory required = 0 MB per parallel sub and 0 MB per instance
2025-05-30 22:13:22.718 member=0 Compute derived parameters
2025-05-30 22:13:22.719 member=0 Prepare for simulation
2025-05-30 22:13:22.721 member=0 Simulation progress=0% cases=0
2025-05-30 22:13:22.722 Welcome to the RiskPaths model!
[back to Release mode examples]
[back to topic contents]
Windows, Visual Studio:
2025-05-30 22:18:13.902 RiskPaths_extra
2025-05-30 22:18:13.910 Model version : 3.0.0.0
2025-05-30 22:18:13.912 Model created : 2025-05-30 22:17:59.802
2025-05-30 22:18:13.913 Model digest : 652f1e74e727a20fc803ce928a75b3bc
2025-05-30 22:18:13.914 OpenM++ version: Development version (build from source code)
2025-05-30 22:18:13.915 OpenM++ build : Windows 64 bit Release
2025-05-30 22:18:13.916 OM_ROOT=C:/Development/X/ompp
2025-05-30 22:18:13.917 Model build : Windows 64 bit Release
2025-05-30 22:18:13.918 Prepare fixed and missing parameters
2025-05-30 22:18:13.919 Run: 2025_05_30_22_18_13_860
2025-05-30 22:18:13.930 Run: 102 RiskPaths_extra_Default
2025-05-30 22:18:13.931 Warning : model can expose microdata at run-time with output_microdata = on
2025-05-30 22:18:13.932 Warning : model will fault on division by zero with fp_exceptions = on
2025-05-30 22:18:13.934 Get scenario parameters for process
2025-05-30 22:18:13.937 member=0 Bind scenario parameters
2025-05-30 22:18:13.938 member=0 Predicted memory required = 0 MB per parallel sub and 0 MB per instance
2025-05-30 22:18:13.939 member=0 Compute derived parameters
2025-05-30 22:18:13.940 member=0 Prepare for simulation
2025-05-30 22:18:13.942 member=0 Simulation progress=0% cases=0
2025-05-30 22:18:13.944 Welcome to the RiskPaths model!
...\RiskPaths_extra.exe (process 26972) exited with code -1073741682 (0xc000008e).
Press any key to close this window . . .
[back to Release mode examples]
[back to topic contents]
Linux, command shell:
$ ./RiskPaths_extra
2025-05-30 22:10:53.941 RiskPaths_extra
2025-05-30 22:10:53.973 Model version : 3.0.0.0
2025-05-30 22:10:53.973 Model created : 2025-05-30 21:32:30.476
2025-05-30 22:10:53.975 Model digest : 652f1e74e727a20fc803ce928a75b3bc
2025-05-30 22:10:53.976 OpenM++ version: Development version (build from source code)
2025-05-30 22:10:53.977 OpenM++ build : Linux Release
2025-05-30 22:10:53.978 OM_ROOT=/mnt/hgfs/X/ompp
2025-05-30 22:10:53.979 Model build : Linux Release
2025-05-30 22:10:53.980 Prepare fixed and missing parameters
2025-05-30 22:10:53.983 Run: 2025_05_30_22_10_53_928
2025-05-30 22:10:54.023 Run: 103 RiskPaths_extra_Default
2025-05-30 22:10:54.024 Warning : model can expose microdata at run-time with output_microdata = on
2025-05-30 22:10:54.025 Warning : model will fault on division by zero with fp_exceptions = on
2025-05-30 22:10:54.026 Get scenario parameters for process
2025-05-30 22:10:54.034 member=0 Bind scenario parameters
2025-05-30 22:10:54.035 member=0 Predicted memory required = 0 MB per parallel sub and 0 MB per instance
2025-05-30 22:10:54.037 member=0 Compute derived parameters
2025-05-30 22:10:54.037 member=0 Prepare for simulation
2025-05-30 22:10:54.038 member=0 Simulation progress=0% cases=0
2025-05-30 22:10:54.039 Welcome to the RiskPaths model!
Floating point exception