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?
- 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.