This doesn't apply to just division by zero.
First, a quick note on terminology. IEEE-754 distinguishes an exception from a trap. An exception is an event that may occur, and a trap is one possible response to that condition occurring.
The IEEE-754 standard defines five exceptions:
- Invalid operation: mathematically undefined (e.g. $\sqrt{-1}$).
- Division by zero: an operation on finite operands gives an exact infinite result. The 1985 revision only defined this for actual division by zero, but later revisions include things like $\log 0$.
- Overflow: a finite result is too large to be represented accurately.
- Underflow: a result is so small that it is outside the normal range.
- Inexact: the exact unrounded result is not representable exactly.
The first three are called "common exceptions", and usually can't be ignored when they occur. The last two, despite not being "common", are in fact extremely common: almost all floating point operations "raise" those exceptions and they can usually be ignored. It's actually quite rare that (say) a division operation returns an exact result.
The IEEE-754 standard specifies that whether or not each kind of exception actually causes a trap can be separately controlled. If the exception's trap is enabled, and the exception occurs, then a platform-specific stuff trap occurs (e.g. SIGFPE
on Unix). If the exception's trap is disabled, then a "reasonable" value is returned and a flag is set recording that the exception occurs.
The exact value returned depends on a bunch of factors:
- Invalid operation returns a quiet
NaN
.
- Division by zero returns positive or negative
Inf
.
- Overflow returns either
Inf
, or the representable number of largest magnitude, depending on the rounding mode.
- Underflow returns a subnormal number or zero (which is technically just a kind of subnormal number), respecting the rounding mode.
- Inexact returns a rounded value.
When you raise a trap, there is then the question as to how this trap is reported. This is an especially important question for automatic vectorisation; suppose you have three parallel division operations that the compiler decides could be performed in a 4-way SIMD unit. Well that's nice, but if division by zero traps are turned on, you had better guarantee that the "unused" SIMD lane contains values that can't trap.
Some architectures, such as Cray-1 and Alpha AXP, made the design decision that, even when programmers want trapping arithmetic exceptions, it's a rare occurrence. Therefore, arithmetic exceptions don't necessarily need to trap in the precise location where they occurred in release code.
The Alpha has a "trap barrier" instruction TRAPB
which checks the trap flags to see if a trap occurred and raise it if appropriate. For debug-mode compilation, the compiler might insert more TRAPB
instructions than it would in a release-mode compilation. This decision makes for a much simpler superscalar CPU, and gives much better power performance, despite not being technically IEEE-754 compliant.
Note that this behaviour fits with the way that programming languages typically define trapping arithmetic handlers: a trap is raised by a high-level language concept such as a statement or a block, rather than a CPU instruction.
NaN
value vs throwing an error for undefined expressions. $\endgroup$float('nan')
also returns the builtin 'NaN' in Python $\endgroup$