Would this test pass or fail?:

``````@Test
fun `adding one tenth ten times equals one`() {
var result: Double = 0.0

repeat(10) {
result += 0.1
}

assert(result == 1.0)
}
``````

It fails! But why?

## Non-decimal base

Floating-point numbers like `Float` and `Double` are internally represented like this:

``````mantissa * base ^ exponent
``````

For humans the base of `10` is very common. We use the decimal system. In the decimal system `0.1` can be easily represented like this:

``````1 * 10^(-1) = 0.1
``````

Computers don’t use a base of `10` but a base of `2`. Also the `mantissa` is stored in a binary format. So for the computer `0.1` looks more like this:

``````1.600000023842… * 2^(-4) = 0.100000001490…
``````

We see that `0.1` is not a “clean number” for the computer. In fact, the computer cannot precisely represent `0.1`. There is a small error. But it’s big enough so that summing up `0.1` ten times doesn’t equal to `1.0` exactly.

There we can already see the second problem: precision.

## Precision

Floating-point numbers are not represented with infinite precision. Instead it’s actually very limited.

Look at this example: What will it print?

``````var x: Float = 100_000f

repeat(1_000) {
x += 0.001f
}

println(x)
``````

Math is telling us:

``````100_000 + 0.001 * 1_000 = 100_001
``````

However, if we run the code it prints:

``````100000.0
``````

Why is that? It’s because we exceed `Float`'s precision already with the first addition: `100_000 + 0.001 = 100_000.001`. `Float` cannot store some many digits, so the least significant digits are cut. As a result we end up with `100_000` again after the addition. That game repeats 1000 times. And we eventually end up with the same number as we started.

We could convert `Float` to `Double`, because it has more precision:

``````var x: Double = 100_000.0

repeat(1_000) {
x += 0.001
}

println(x)
``````

It prints:

``````100001.00000000384
``````

So it worked now, but it has a very small error (the additional `0.00000000384`) because of the non-decimal base. However, `Double` suffers from the same problem. The only difference is that the distance of the numbers has to be larger for the problem to surface. Try `100_000_000_000_000.0` as staring number and you’ll see we end up with the same precision problem.

## Equality of floating-point numbers

The problems of non-decimal base and limited precision are inherent in floating-point numbers. Because of them, floating-point numbers are never used when dealing with money in software. It depends on your specific problem if such errors are acceptable. There are alternatives, like fixed-point numbers, however, they have other limitations.

If your domain can accept the limitations of floating-point numbers, you might still need to compare two numbers. The trick here is to not compare equality but to check if the two numbers differ within an acceptable range. You have to define a precision that is appropriate to your domain.

Coming back to the very first example: Let’s assume we’re dealing with lengths in meters here. In our domain an error of 1mm is acceptable, our test could look like this:

``````@Test
fun `adding 10cm ten times equals 1m`() {
var lengthInMeters: Double = 0.0

repeat(10) {
lengthInMeters += 0.1 // = 10cm
}

val expectedLengthInMeters = 1.0
val precisionInMeters = 0.001 // = 1mm

if (abs(expectedLengthInMeters - lengthInMeters) < precisionInMeters) {
// `expectedLengthInMeters` and `lengthInMeters` are equal
// (within `precisionInMeters`)
} else {
fail("\$expectedLengthInMeters and \$lengthInMeters are different!")
}
}
``````

We can also extract the “equality logic” into a dedicated function:

``````fun Double.equals(other: Double, precision: Double) =
abs(this - other) < precision
``````

It can be used like:

``````if (expectedLengthInMeters.equals(lengthInMeters, precision = precisionInMeters)) { …
``````

Note: the `abs` is necessary so it doesn’t matter which value is bigger and which is smaller. Then the check also works correctly when `this` and `other` are swapped, as the difference will never be negative.

I hope this post helped you to understand why it’s a bad idea to compare two floating-point numbers using `==` and how to avoid errors in that regard. Also have a look at this Floating Point Converter/Calculator. It can be used to understand the binary representation of Floats even better.