The Behaviour of Floating Point Division by Zero

The behaviour of floating point division by zero

Division by zero both integer and floating point are undefined behavior [expr.mul]p4:

The binary / operator yields the quotient, and the binary % operator yields the remainder from the division
of the first expression by the second. If the second operand of / or % is zero the behavior is undefined. ...

Although implementation can optionally support Annex F which has well defined semantics for floating point division by zero.

We can see from this clang bug report clang sanitizer regards IEC 60559 floating-point division by zero as undefined that even though the macro __STDC_IEC_559__ is defined, it is being defined by the system headers and at least for clang does not support Annex F and so for clang remains undefined behavior:

Annex F of the C standard (IEC 60559 / IEEE 754 support) defines the
floating-point division by zero, but clang (3.3 and 3.4 Debian snapshot)
regards it as undefined. This is incorrect:


Support for Annex F is optional, and we do not support it.

#if STDC_IEC_559


This macro is being defined by your system headers, not by us; this is
a bug in your system headers. (FWIW, GCC does not fully support Annex
F either, IIRC, so it's not even a Clang-specific bug.)

That bug report and two other bug reports UBSan: Floating point division by zero is not undefined and clang should support Annex F of ISO C (IEC 60559 / IEEE 754) indicate that gcc is conforming to Annex F with respect to floating point divide by zero.

Though I agree that it isn't up to the C library to define STDC_IEC_559 unconditionally, the problem is specific to clang. GCC does not fully support Annex F, but at least its intent is to support it by default and the division is well-defined with it if the rounding mode isn't changed. Nowadays not supporting IEEE 754 (at least the basic features like the handling of division by zero) is regarded as bad behavior.

This is further support by the gcc Semantics of Floating Point Math in GCC wiki which indicates that -fno-signaling-nans is the default which agrees with the gcc optimizations options documentation which says:

The default is -fno-signaling-nans.

Interesting to note that UBSan for clang defaults to including float-divide-by-zero under -fsanitize=undefined while gcc does not:

Detect floating-point division by zero. Unlike other similar options, -fsanitize=float-divide-by-zero is not enabled by -fsanitize=undefined, since floating-point division by zero can be a legitimate way of obtaining infinities and NaNs.

See it live for clang and live for gcc.

C++ warning: division of double by zero

Floating point division by zero is well defined by IEEE and gives infinity (either positive or negative according to the value of the numerator (or NaN for ±0) ).

For integers, there is no way to represent infinity and the language defines the operation to have undefined behaviour so the compiler helpfully tries to steer you clear from that path.

However in this case, since the numerator is a double, the divisor (0) should be promoted to a double too and there's no reason to give a warning here whilst not giving a warning for 0.0 so I think this is a compiler bug.

Why does dividing by zero for Float not raise an exception in Ruby?

As with almost all programming languages, ruby's implementation of floating-point arithmetic is compliant to the IEEE 754 standard.

This specification (linked above) defines five exceptions that must (at least by default) be handled in a specific way:

  • Invalid operation: mathematically undefined, e.g., the square root of a negative number. By default, returns qNaN.

  • Division by zero: an operation on finite operands gives an exact infinite result, e.g., 1/0 or log(0). By default, returns ±infinity.

  • Overflow: a result is too large to be represented correctly (i.e., its exponent with an unbounded exponent range would be larger than emax). By default, returns ±infinity for the round-to-nearest modes (and follows the rounding rules for the directed rounding modes).

  • Underflow: a result is very small (outside the normal range) and is inexact. By default, returns a subnormal or zero (following the rounding rules).

  • Inexact: the exact (i.e., unrounded) result is not representable exactly. By default, returns the correctly rounded result.

Therefore 1.0/0 must equal +Infinity, and 0.0/0 must equal NaN.


Integer objects do not conform to the above standard. (There is no Infinity or NaN, and all operations are exact.) Therefore, it was a language-specific decision to raise an exception for operations such as 1/0.

Why does division near to zero have different behaviors in python?

This is related to the IEEE754 floating-point format itself, not so much Python's implementation of it.

Generally speaking, floats can represent smaller negative exponents than large positive ones, because of denormal numbers. This is where the mantissa part of the float is no longer implicitly assumed to begin with a 1, but rather describes the entire mantissa, and begins with zeroes. In case you don't know what that is, I'd suggest you read up about how floats are represented, perhaps starting here.

Because of this, when you invert a denormal number, you may end up with a positive exponent too large to represent. The computer then gives you inf in its place. The 1e-308 in your example is actually also denormal, but still not small to overflow when inverted (because among normal numbers, the standard actually allows for slightly larger positive than negative exponents).

In case of 1e-324, that number is simply too small to be represented even as a denormal, so that float literal is effectively equal to zero. That's why you get division by zero. The smallest representable 64-bit float is (slightly below) 5e-324.

Floating point division by zero not constexpr

Infinity is an implementation defined result, the standard does not require IEEE floating point and division by zero is formally undefined behavior and constant expression have an exclusion for undefined behavior.

From the draft C++ standard section 5.6 [expr.mul]:

The binary / operator yields the quotient, and the binary % operator
yields the remainder from the division of the first expression by the
second. If the second operand of / or % is zero the behavior is
undefined.

Can a near-zero floating value cause a divide-by-zero error?

Floating point division by zero is not an error. It raises a floating point exception (which is a no-op unless you're actively checking them) on implementations that support floating point exceptions, and has well-defined result: either positive or negative infinity (if the numerator is nonzero), or NAN (if the numerator is zero).

It's also possible to get infinity (and an overflow exception) as the result when the denominator is nonzero but very close to zero (e.g. subnormal), but again this is not an error. It's just how floating point works.

Edit: Note that, as Eric has pointed out in the comments, this answer assumes the requirements of Annex F, an optional part of the C standard detailing floating point behavior and aligning it with the IEEE standard for floating point. In the absence of IEEE arithmetic, C does not define floating point division by zero (and in fact, the results of all floating point operations are implementation-defined and may be defined as complete nonsense and still conform to the C standard), so if you're dealing with an outlandish C implementation that does not honor IEEE floating point, you'll have to consult the documentation for the implementation you're using to answer this question.

Handling division by zero with csignal results in unexpected behaviour

It's defined as:

The SIGFPE signal reports a fatal arithmetic error. Although the name is derived from “floating-point exception”, this signal actually covers all arithmetic errors, including division by zero and overflow. If a program stores integer data in a location which is then used in a floating-point operation, this often causes an “invalid operation” exception, because the processor cannot recognize the data as a floating-point number.

There's no reason for it to be labelled specifically FPE but these sorts of labels can evolve in unpredictable ways. I wouldn't read too much into it.

These signals are part of the POSIX standard and may not be fully supported or implemented in Windows. The Windows implementation of these support facilities is lacking in a number of areas, like how fork() is unsupported.

Define division by zero as infinity

If you require this behaviour, use floating point numbers, which can represent infinity, and provide the desired behaviour. Note that technically this is undefined behaviour but in practice most compilers (all mainstream compilers for standard architectures) implement IEEE 754 semantics, e.g. GCC.

int main() {
float f = 42;
float g = f / 0.0f;
printf("%f\n", g);
}

Output:

inf

This is behaviour that can be relied on since it’s clearly documented by the compilers. However, when writing portable code make sure that you test these assumptions inside your code (e.g. by testing whether the preprocessor macro __STDC_IEC_559__, as well as compiler-specific macros are defined).

If, for some reason, you need this behaviour for integer values, the only recourse is to make your own type. Something like this:

typedef struct {
int value;
bool is_inf;
bool is_nan;
} ext_int;

ext_int make_ext_int(int i) {
return (ext_int) {i, false, false};
}

ext_int make_nan() {
return (ext_int) {0, false, true};
}

ext_int make_inf(int sign) {
return (ext_int) {(sign > 0) - (sign < 0), true, false};
}

ext_int ext_div(ext_int a, ext_int b) {
if (a.is_nan || b.is_nan) {
return make_nan();
}
if (b.value == 0) {
return make_inf(a.value);
}
// TODO: insert other cases.
return (ext_int) {a.value / b.value, false, false};
}

… in a real implementation you’d pack the different flags rather than having a separate bool for each, of course.

Floating point division by zero exception in Delphi5

I just tried the following code:

procedure TTTest.FormCreate(Sender: TObject);
var v: extended;
one: extended;
begin
one := 1.0;
v := 100*Power(1.25,one);
end;

It just compiles and runs as expected in Delphi 5.

My guess is that the division per zero flag may be set outside your code (even if you do not link to C++ code, calling Direct X or such may have the same effect), but raised later, in _Frac.

The only call to Frac in the standard implementation of Power() is to test Frac(Exponent) = 0.0.

There was a modification in the implementation of Frac between Delphi 5 and Delphi 6.

Here is the Delphi 5 version:

procedure       _FRAC;
asm
FLD ST(0)
SUB ESP,4
FSTCW [ESP]
FWAIT
FLDCW cwChop
FRNDINT
FWAIT
FLDCW [ESP]
ADD ESP,4
FSUB
end;

Here is the Delphi 6 version:

procedure       _FRAC;
asm
FLD ST(0)
SUB ESP,4
FNSTCW [ESP].Word // save
FNSTCW [ESP+2].Word // scratch
FWAIT
OR [ESP+2].Word, $0F00 // trunc toward zero, full precision
FLDCW [ESP+2].Word
FRNDINT
FWAIT
FLDCW [ESP].Word
ADD ESP,4
FSUB
end;

From the above code, you'll find out that the following commands caused the delayed exceptions to be raised before Delphi 6 was released: Trunc, Frac, Ceil.

So I guess you faced an issue with Delphi 5, which has been fixed with Delphi 6. You may have to use your own version of Power, like this one:

function Power(Base, Exponent: Extended): Extended;
begin
if Exponent = 0.0 then
Result := 1.0 { n**0 = 1 }
else if (Base = 0.0) and (Exponent > 0.0) then
Result := 0.0 { 0**n = 0, n > 0 }
else
Result := Exp(Exponent * Ln(Base))
end;


Related Topics



Leave a reply



Submit