Golang : Accurate and reliable decimal calculations
If you have been programming for a long time, chances are you will be aware of a well-known issue with the floating-point numbers. The "feature" or inaccuracy is that the floating-point numbers cannot be accurately represented by all base-10 decimals. This effects all programming languages and not isolated to Golang.
Let's consider the following code:
package main
import (
"fmt"
)
func main() {
// not accurate
a := 5.2
b := 4.1
fmt.Println(a + b)
fmt.Println((a + b) == 9.3) // will return TRUE
c := 5.2
d := 2.1
fmt.Println(c + d)
fmt.Println((c + d) == 7.3) // will return FALSE
}
and running the program will produce this output:
9.3
true
7.300000000000001
false
As you can see that the calculations are not reliable and inaccurate for the certain decimal values. The inaccuracy is caused by the underlying CPU and the native representation used by Golang (which is faster and optimized for performing a large number of calculations).
To get accuracy in performing decimal calculations, you can use the github.com/shopspring/decimal package.
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
// accurate
aDec, _ := decimal.NewFromString("5.2")
bDec, _ := decimal.NewFromString("4.1")
fmt.Println(aDec.Add(bDec))
cDec, _ := decimal.NewFromString("9.3")
// wrong way to compare
fmt.Println((aDec.Add(bDec)) == cDec) // will still return FALSE
dDec := aDec.Add(bDec)
fmt.Println(dDec)
// still wrong way to compare
fmt.Println(dDec == cDec) // will still return FALSE
// proper way to compare
// see https://godoc.org/github.com/shopspring/decimal#Decimal.Cmp
// -1 if cDec < dDec
// 0 if cDec == dDec
// +1 if cDec > dDec
result := cDec.Cmp(dDec)
fmt.Println(cDec.Cmp(dDec)) // should return 0
if result == 0 {
fmt.Println("true")
}
}
Output:
9.3
false
9.3
false
0
true
For Golang developers that are more comfortable with the standard package such as math/big package.
package main
import (
"fmt"
"math/big"
)
func main() {
f1 := big.NewFloat(5.2)
f2 := big.NewFloat(2.1)
var f3, f4 big.Float
f3.Add(f1, f2)
fmt.Println(f3.String())
f4.Add(f1, f2)
fmt.Println(f3.Cmp(&f4)) // should return 0 and zero means equal
}
Output:
7.3
0
Depending on your application domain, most developers will use the standard float
..... however, if you are programming applications for science and engineering domain that demand accuracy. Please do consider testing your application for the decimal "feature" or inaccuracy and use the appropriate package to address the inaccuracy.
References:
https://github.com/golang/go/issues/12127
https://godoc.org/github.com/shopspring/decimal#Decimal.Cmp
https://www.socketloop.com/tutorials/golang-compare-floating-point-numbers
See also : Golang : Compare floating-point numbers
By Adam Ng
IF you gain some knowledge or the information here solved your programming problem. Please consider donating to the less fortunate or some charities that you like. Apart from donation, planting trees, volunteering or reducing your carbon footprint will be great too.
Advertisement
Tutorials
+13.7k Golang: Pad right or print ending(suffix) zero or spaces in fmt.Printf example
+12k Golang : How to display image file or expose CSS, JS files from localhost?
+8.9k nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
+9.7k Golang : Setting variable value with ldflags
+6.9k Golang : Validate credit card example
+17.6k Golang : Simple client server example
+7.3k Javascript : Push notifications to browser with Push.js
+12.8k Golang : Handle or parse date string with Z suffix(RFC3339) example
+39.7k Golang : Convert to io.ReadSeeker type
+10.2k Golang : Meaning of omitempty in struct's field tag
+9.4k Golang : Validate IPv6 example
+9.6k Golang : Get current, epoch time and display by year, month and day