-
Notifications
You must be signed in to change notification settings - Fork 0
Floating Point Exceptions
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
- Windows: Quick Start for Model Users
- Windows: Quick Start for Model Developers
- Linux: Quick Start for Model Users
- Linux: Quick Start for Model Developers
- MacOS: Quick Start for Model Users
- MacOS: Quick Start for Model Developers
- Model Run: How to Run the Model
- MIT License, Copyright and Contribution
- Model Code: Programming a model
- Windows: Create and Debug Models
- Linux: Create and Debug Models
- MacOS: Create and Debug Models
- MacOS: Create and Debug Models using Xcode
- Modgen: Convert case-based model to openM++
- Modgen: Convert time-based model to openM++
- Modgen: Convert Modgen models and usage of C++ in openM++ code
- Model Localization: Translation of model messages
- How To: Set Model Parameters and Get Results
- Model Run: How model finds input parameters
- Model Output Expressions
- Model Run Options and ini-file
- OpenM++ Compiler (omc) Run Options
- OpenM++ ini-file format
- UI: How to start user interface
- UI: openM++ user interface
- UI: Create new or edit scenario
- UI: Upload input scenario or parameters
- UI: Run the Model
- UI: Use ini-files or CSV parameter files
- UI: Compare model run results
- UI: Aggregate and Compare Microdata
- UI: Filter run results by value
- UI: Disk space usage and cleanup
- UI Localization: Translation of openM++
-
Highlight: hook to self-scheduling or trigger attribute
-
Highlight: The End of Start
-
Highlight: Enumeration index validity and the
index_errors
option -
Highlight: Simplified iteration of range, classification, partition
-
Highlight: Parameter, table, and attribute groups can be populated by module declarations
- Oms: openM++ web-service
- Oms: openM++ web-service API
- Oms: How to prepare model input parameters
- Oms: Cloud and model runs queue
- Use R to save output table into CSV file
- Use R to save output table into Excel
- Run model from R: simple loop in cloud
- Run RiskPaths model from R: advanced run in cloud
- Run RiskPaths model in cloud from local PC
- Run model from R and save results in CSV file
- Run model from R: simple loop over model parameter
- Run RiskPaths model from R: advanced parameters scaling
- Run model from Python: simple loop over model parameter
- Run RiskPaths model from Python: advanced parameters scaling
- Windows: Use Docker to get latest version of OpenM++
- Linux: Use Docker to get latest version of OpenM++
- RedHat 8: Use Docker to get latest version of OpenM++
- Quick Start for OpenM++ Developers
- Setup Development Environment
- 2018, June: OpenM++ HPC cluster: Test Lab
- Development Notes: Defines, UTF-8, Databases, etc.
- 2012, December: OpenM++ Design
- 2012, December: OpenM++ Model Architecture, December 2012
- 2012, December: Roadmap, Phase 1
- 2013, May: Prototype version
- 2013, September: Alpha version
- 2014, March: Project Status, Phase 1 completed
- 2016, December: Task List
- 2017, January: Design Notes. Subsample As Parameter problem. Completed
GET Model Metadata
- GET model list
- GET model list including text (description and notes)
- GET model definition metadata
- GET model metadata including text (description and notes)
- GET model metadata including text in all languages
GET Model Extras
GET Model Run results metadata
- GET list of model runs
- GET list of model runs including text (description and notes)
- GET status of model run
- GET status of model run list
- GET status of first model run
- GET status of last model run
- GET status of last completed model run
- GET model run metadata and status
- GET model run including text (description and notes)
- GET model run including text in all languages
GET Model Workset metadata: set of input parameters
- GET list of model worksets
- GET list of model worksets including text (description and notes)
- GET workset status
- GET model default workset status
- GET workset including text (description and notes)
- GET workset including text in all languages
Read Parameters, Output Tables or Microdata values
- Read parameter values from workset
- Read parameter values from workset (enum id's)
- Read parameter values from model run
- Read parameter values from model run (enum id's)
- Read output table values from model run
- Read output table values from model run (enum id's)
- Read output table calculated values from model run
- Read output table calculated values from model run (enum id's)
- Read output table values and compare model runs
- Read output table values and compare model runs (enun id's)
- Read microdata values from model run
- Read microdata values from model run (enum id's)
- Read aggregated microdata from model run
- Read aggregated microdata from model run (enum id's)
- Read microdata run comparison
- Read microdata run comparison (enum id's)
GET Parameters, Output Tables or Microdata values
- GET parameter values from workset
- GET parameter values from model run
- GET output table expression(s) from model run
- GET output table calculated expression(s) from model run
- GET output table values and compare model runs
- GET output table accumulator(s) from model run
- GET output table all accumulators from model run
- GET microdata values from model run
- GET aggregated microdata from model run
- GET microdata run comparison
GET Parameters, Output Tables or Microdata as CSV
- GET csv parameter values from workset
- GET csv parameter values from workset (enum id's)
- GET csv parameter values from model run
- GET csv parameter values from model run (enum id's)
- GET csv output table expressions from model run
- GET csv output table expressions from model run (enum id's)
- GET csv output table accumulators from model run
- GET csv output table accumulators from model run (enum id's)
- GET csv output table all accumulators from model run
- GET csv output table all accumulators from model run (enum id's)
- GET csv calculated table expressions from model run
- GET csv calculated table expressions from model run (enum id's)
- GET csv model runs comparison table expressions
- GET csv model runs comparison table expressions (enum id's)
- GET csv microdata values from model run
- GET csv microdata values from model run (enum id's)
- GET csv aggregated microdata from model run
- GET csv aggregated microdata from model run (enum id's)
- GET csv microdata run comparison
- GET csv microdata run comparison (enum id's)
GET Modeling Task metadata and task run history
- GET list of modeling tasks
- GET list of modeling tasks including text (description and notes)
- GET modeling task input worksets
- GET modeling task run history
- GET status of modeling task run
- GET status of modeling task run list
- GET status of modeling task first run
- GET status of modeling task last run
- GET status of modeling task last completed run
- GET modeling task including text (description and notes)
- GET modeling task text in all languages
Update Model Profile: set of key-value options
- PATCH create or replace profile
- DELETE profile
- POST create or replace profile option
- DELETE profile option
Update Model Workset: set of input parameters
- POST update workset read-only status
- PUT create new workset
- PUT create or replace workset
- PATCH create or merge workset
- DELETE workset
- POST delete multiple worksets
- DELETE parameter from workset
- PATCH update workset parameter values
- PATCH update workset parameter values (enum id's)
- PATCH update workset parameter(s) value notes
- PUT copy parameter from model run into workset
- PATCH merge parameter from model run into workset
- PUT copy parameter from workset to another
- PATCH merge parameter from workset to another
Update Model Runs
- PATCH update model run text (description and notes)
- DELETE model run
- POST delete model runs
- PATCH update run parameter(s) value notes
Update Modeling Tasks
Run Models: run models and monitor progress
Download model, model run results or input parameters
- GET download log file
- GET model download log files
- GET all download log files
- GET download files tree
- POST initiate entire model download
- POST initiate model run download
- POST initiate model workset download
- DELETE download files
- DELETE all download files
Upload model runs or worksets (input scenarios)
- GET upload log file
- GET all upload log files for the model
- GET all upload log files
- GET upload files tree
- POST initiate model run upload
- POST initiate workset upload
- DELETE upload files
- DELETE all upload files
Download and upload user files
- GET user files tree
- POST upload to user files
- PUT create user files folder
- DELETE file or folder from user files
- DELETE all user files
User: manage user settings
Model run jobs and service state
- GET service configuration
- GET job service state
- GET disk usage state
- POST refresh disk space usage info
- GET state of active model run job
- GET state of model run job from queue
- GET state of model run job from history
- PUT model run job into other queue position
- DELETE state of model run job from history
Administrative: manage web-service state
- POST a request to refresh models catalog
- POST a request to close models catalog
- POST a request to close model database
- POST a request to delete the model
- POST a request to open database file
- POST a request to cleanup database file
- GET the list of database cleanup log(s)
- GET database cleanup log file(s)
- POST a request to pause model run queue
- POST a request to pause all model runs queue
- PUT a request to shutdown web-service