Infinity in C Floating-Point Arithmetic

Martin LichtMartin Licht
4 min read

The C programming language adopts the IEEE 754 floating-point arithmetic. This includes the representation of non-finite floating-point values: on the one hand, there are positive and negative infinity. On the other hand, there is NaN (Not a Number), signifying invalid arithmetical operations.

This post discusses the infinities in more detail. There are two different infinities in floating-point arithmetic.

Positive infinity (+∞) represents a value larger than any finite value of the floating-point type. It results from adding and multiplying two very large numbers, or from the division of a finite positive value by zero.

Negative infinity (−∞) is very similar but negative. For example, it results from the division of a negative finite value by zero.

Notice that technically, each floating-point type (float, double, long double) has its own version of positive and negative infinity. However, casting values between floating-point numbers preserves +/- infinity.

Arithmetic and comparisons with infinities

To understand the purpose of these floating-point types, let us first study their arithmetic behavior. Via IEEE 754, the following behaviors are required.

The infinities behave completely intuitively in the basic arithmetic operations.

  • finite ± ±∞ = ±∞
  • finite × ±∞ = ±∞ unless the finite value is ±0
  • ±∞ × ±∞ = ±∞
  • finite / ∞ = ±0

Some arithmetic operations involving infinities do not have an intuitive value (at least, there is no general consensus what these operations should have). In that case, the result will be NaN:

  • ∞ + (−∞) = NaN
  • ±0 / ±∞ = NaN
  • ±∞ / ±0 = NaN
  • ±∞ / ±∞ = NaN

The comparisons involving infinities behave expected. +∞ is greater than all finite numbers, and −∞ is less than all finite numbers. Naturally, +∞ is greater than −∞. And as one would expect,

  • +∞ == +∞
  • −∞ == −∞
  • +∞ != −∞

Bit Representations of infinities

Let us recapitulate how infinities are represented in the floating-point standard. Floating-point numbers in any binary IEEE 754 format consist of the three fields of bits:

  • Sign bit, where 0 designates positive and 1 designates negative.
  • Exponent field, which encodes the exponent of the floating-number
  • Mantissa field, which contains the (non-leading) bit representation of the mantissa

The non-finite floating values are encoded by special values of the exponent: if all exponent bits are set to 1, then the floating-point number is non-finite.

The different types of non-finite values are then distinguished by the bit values in the mantissa and the the sign bit. Infinities are encoded by the setting all exponent bits to 1 and all mantissa bits to to 0. The sign bit distinguishes between positive (0) and negative infinities (1).

Effectively, the IEEE 754 format sacrifices one order of magnitude in each floating-type to represent the non-finite values. That is not a huge sacrifice: the uttermost order of magnitude of a floating-point value is rarely reached, and if computations ever operate within that order of magnitude, then one should consider switching to a larger floating-point type (or arbitrary precision packages) to avoid undesired overflows.

With 32-bit precision, when the sign bit is followed by the exponent field and then the mantissa, the bit patterns for the infinities look as follows.

+∞: 0 11111111 00000000000000000000000
−∞: 1 11111111 00000000000000000000000

Purpose of infinities

The implementation of infinities requires some effort in chip design.

Typically, operations involving infinities are assumed to be rare and hence the floating-point circuitry is not optimized for such values. If your numerical computations suddenly slow down, it might that a non-finite floating-point value has occurred and propagated through the calculations.

So what is a justification to include non-finite values such as infinity in the floating-point standard? Here are some reasons

  • Overflow Behavior: we would like to handle the case where the result of an operation is not representable within the floating-point type. Including infinities seems to be a smoother choice than (sign changing) wrap around and triggering hardware exceptions.
  • Division by zero: rather than, say, triggering undefined behavior or a hardware exception, we can represent the result of such a division as a regular arithmetic value.
  • Representation of asymptotics: infinities can encode the limit behavior of numerical operations. For example, tan(π/2) = ∞ or 1.0/∞ = 0.0.
  • Order preservation: the infinities +∞ and −∞ have meaningful order relationship with the finite values.
  • Exception signaling: it avoids exceptions on operations that meaningfully produce infinities.
  • Continuation of computations: This is perhaps the most important feature: instead of triggering an exception, the control flow proceeds reliably.
0
Subscribe to my newsletter

Read articles from Martin Licht directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Martin Licht
Martin Licht