1. Introduction
It seems like if a computer is "smart," then 0.1 + 0.2 should just be 0.3. But that's not exactly how it works. Let's break it down with a simple example.
double x = 0.1;
double y = 0.2;
double sum = x + y;
Console.WriteLine(sum); // What will the program print?
Now try comparing it to 0.3:
Console.WriteLine(sum == 0.3); // Will this be true or false?
If you see False, don't be surprised!
The reason is how numbers are stored in memory
Computers work with numbers in binary. But not all decimal fractions can be represented as a finite binary fraction, just like 1/3 can't be written exactly as a decimal (0.333...). For example, 0.1 in binary is an infinite fraction, so it has to be "rounded" to fit in memory.
To put it in plain English, double sometimes "pretends" to store your number exactly, but it's actually just holding a really close approximation.
2. What kind of "weirdness" can happen with double arithmetic?
Let's check out some real-life examples.
Example 1. The classic "magic" of 0.1 + 0.2
double a = 0.1;
double b = 0.2;
double sum = a + b;
Console.WriteLine(sum); // 0.30000000000000004
Console.WriteLine(sum == 0.3); // False
The computer didn't print 0.3, but 0.30000000000000004. The difference is tiny, but if you're working with, say, finances — that's a big deal.
Example 2. Iterative addition
double result = 0;
for (int i = 0; i < 10; i++)
{
result += 0.1;
}
Console.WriteLine(result); // 0.9999999999999999
You wanted 1.0 — but you got just a bit less. Again, it's all about rounding inside double.
Why this matters in real-world tasks
A lot of people think: "Who cares, it's just a tiny error, whatever!" Let's look at an example from the world of payments.
Say your online bank adds up 100 transactions of 0.1 euro each. If your program "loses" one hundred-thousandth of a euro on every iteration, at the scale of a bank you're "losing" real money. That's when the accountant will come to you and ask: "Where's our money?!"
3. How to properly compare floating-point numbers
Since double often can't store exactly the value you expect, comparing directly with == can let you down. Instead, it's common to compare the absolute difference between numbers with some really tiny number (epsilon).
Example: comparing with a tolerance
double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 0.000001;
if (Math.Abs(a - b) < epsilon)
{
Console.WriteLine("Almost equal!"); // This is a safer way to compare
}
Here we're saying: "If the difference between the numbers is less than one millionth, let's call them equal."
4. Special double values: Infinity, NaN, -Infinity
The double type stores not just numbers, but also some special values. These pop up in situations that any math teacher would totally forbid.
Infinity (Infinity)
What happens if you divide 1 by 0?
double result = 1.0 / 0.0;
Console.WriteLine(result); // Infinity
In C# (and lots of other languages), dividing by 0 for double doesn't throw an exception! Instead, the result becomes a special value called "positive infinity."
Negative infinity (-Infinity)
If you divide a negative number by 0, you get negative infinity:
double result = -1.0 / 0.0;
Console.WriteLine(result); // -Infinity
"Not a Number" (NaN — Not a Number)
If you do something really weird, like try to take the square root of a negative number:
double result = Math.Sqrt(-1);
Console.WriteLine(result); // NaN
Or the result of dividing 0.0 / 0.0:
double result = 0.0 / 0.0;
Console.WriteLine(result); // NaN
NaN — that's "anything that wouldn't be a number in real life."
Checking for special values
C# has functions to check for special values:
Console.WriteLine(double.IsInfinity(result)); // true if it's infinity
Console.WriteLine(double.IsNaN(result)); // true if it's NaN
Table: How double reacts to unusual operations
| Operation | Result | What gets stored in double |
|---|---|---|
|
Infinity | +∞ |
|
-Infinity | -∞ |
|
NaN | Not a number |
|
NaN | Not a number |
GO TO FULL VERSION