Inaccurate float32 and float64: how to avoid the trap in Go (golang)

Wacław The Developer
2 min readDec 14, 2021
Photo by Luis Villasmil on Unsplash

Hi everyone! Let’s talk about floats today. So, let’s take a look on that simple code:

var n float64 = 0
for i := 0; i < 10; i++ {
n += 0.1
}
fmt.Println(n)
println(n == 1)

You will be surprised, but the output will be

0.9999999999999999
false

So, what about float32?

var n float32 = 0
for i := 0; i < 10; i++ {
n += 0.1
}
fmt.Println(n)
println(n == 1)

Maybe you will be confused now, but the output will be

1.0000001
false

What is going on?

If try to be short — it is the side effect of how CPU represents the floats.

What will be if i will skip this problem?

  1. You will fail the comparison of values. For example, if you are trying to write progress checks — you will be stuck in a loop (sure this is not the best example):
var progressPercentage float64 = 0
for ; ; {
//Do some work and add to overall progress 1%
progressPercentage += 0.01

//Check if 100% reached (1 will never be reached. Just < or > than 1)
if progressPercentage == 1 {
break
}
}

2. Type conversion between systems (for example between DB and your server) may lead to difference in values. You can imagine what will be with financial reports if statistical data will collect the error many times.

What the solution?

There are many topics for different languages. For example, this topic on Microsoft webpage describes what to do for C developers: https://docs.microsoft.com/en-us/cpp/build/why-floating-point-numbers-may-lose-precision?view=msvc-170

As the easy solution for critical parts of app i found useful this Go lib:

So let’s refactor our code:

package main

import "fmt"
import "github.com/shopspring/decimal"

func
main() {
m := decimal.NewFromFloat(0)
for i := 0; i < 100; i++ {
m = m.Add(decimal.NewFromFloat(0.01))
}
fmt.Println(m)
}

Now, the output will be

1

Thanks for reading. I hope it was interesting.

--

--