2.2 Advanced notes on Floating-point types

In this article

  1. Special Float and Double Values
  2. Dividing by Zero
  3. NaN variants
  4. Double Versus Decimal
  5. Real Number Rounding Errors

Special Float and Double Values

Floating-point types have values that certain operations treat specially. These special values are:

  • NaN (Not a Number)
  • +∞ (PositiveInfinity)
  • −∞ (NegativeInfinity)
  • −0 (-0.0 and -0.0f for float)
  • MaxValue
  • MinValue
  • Epsilon
    The float and double classes have constants for the previous constants, for example:
Console.WriteLine (double.NegativeInfinity); // -Infinity

Dividing by Zero

  • Dividing a non-zero number by zero results in an infinite value:
    Console.WriteLine ( 1.0 / 0.0); // Infinity
    Console.WriteLine (−1.0 / 0.0); // -Infinity
    Console.WriteLine ( 1.0 / −0.0); // -Infinity
    Console.WriteLine (−1.0 / −0.0); // Infinity
  • Dividing zero by zero, or subtracting infinity from infinity, results in a NaN:
Console.WriteLine ( 0.0 / 0.0); // NaN
Console.WriteLine ((1.0 / 0.0) − (1.0 / 0.0)); // NaN

NaN variants

  • Using == with a NaN value is never equal to any other value, even another NaN value:
Console.WriteLine (0.0 / 0.0 == double.NaN); // False

To test whether a value is NaN, you must use the float.IsNaN or double.IsNaN method

For example

Console.WriteLine (double.IsNaN (0.0 / 0.0)); // True
  • Using object.Equals, however, two NaN values are equal:
Console.WriteLine (object.Equals (0.0 / 0.0, double.NaN)); // True
  • NaNs are sometimes useful in representing special values. In Windows Presentation Foundation (WPF), double.NaN represents a measurement whose value is “Automatic.”
  • Another way to represent such a value is with a nullable type another is with a custom struct that wraps a numeric type and adds an additional field.

Double Versus Decimal

  • double is useful for scientific computations (such as computing spatial coordinates).
  • decimal is useful for financial computations and values that are “human-made” rather than the result of real-world measurements.
Categorydoubledecimal
Internal representationBase 2Base 10
Decimal precision15–16 significant figures28–29 significant figures
Range±(~10^{-324} \quad to \quad 10^{308})±(~10^{-28} \quad to \quad 10^{28})
Special values+0, −0, +∞, −∞, and NaNNone
SpeedNative to processorNon-native to processor (about 10 times slower than double)

Real Number Rounding Errors

float and double internally represent numbers in base 2. For this reason, only numbers expressible in base-2 are represented precisely. Practically, this means most literals with a fractional component (which are in base 10) will not be represented precisely; for example:

float x = 0.1f; // Not quite 0.1
Console.WriteLine (x + x + x + x + x + x + x + x + x + x); // 1.0000001

This is why float and double are bad for financial calculations. In contrast, decimal works in base 10 and so can precisely represent numbers expressible in base 10 (as well as its factors, base 2 and base 5).

Because real literals are in base 10, decimal can precisely represent numbers such as “0.1“. However, neither double nor decimal can precisely represent a fractional number whose base 10 representation is recurring:

decimal m = 1M / 6M; // 0.1666666666666666666666666667M
double d = 1.0 / 6.0; // 0.16666666666666666
This leads to accumulated rounding errors:
decimal notQuiteWholeM = m+m+m+m+m+m; // 1.0000000000000000000000000002M
double notQuiteWholeD = d+d+d+d+d+d; // 0.99999999999999989

which break equality and comparison operations:

Console.WriteLine (notQuiteWholeM == 1M); // False
Console.WriteLine (notQuiteWholeD < 1.0); // True

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s