Reputation: 2669
I'm interested in a way to accurately subtract 2 float's in Go.
I've tried to use the math/big
library but I can't get an accurate result.
I've used the big.js library in Javascript which solves this problem. Is there a similar library/method for Go arithmetic?
package main
import (
"fmt"
"math/big"
)
func main() {
const prec = 200
a := new(big.Float).SetPrec(prec).SetFloat64(5000.0)
b := new(big.Float).SetPrec(prec).SetFloat64(4000.30)
result := new(big.Float).Sub(a, b)
fmt.Println(result)
}
Result: 999.6999999999998181010596454143524169921875
https://play.golang.org/p/vomAr87Xln
Upvotes: 4
Views: 17497
Reputation: 656
The accepted answer is not accurate. I just ran into this issue and after experimenting with multiple float values and operations I have concluded that you absolutely cannot rely on float precision like this in terms of integer digits or digits after the decimal point. I was able to break the consistency no matter my approach using either addition, subtraction, or multiplication. This is true regardless of whether you use float64
type or big.Float
type. You simply will not have consistent accuracy to a large number of integer or decimal places.
For this reason, API's that deal with large precision numbers, such as Etherscan always return integers in string form and you MUST do the same. You will have to rely on the big.Int
type exclusively and then display the desired decimal value at the very end of your process (i.e. when printing to console or displaying on a browser).
Again, when printing you cannot do mathematical operations to display the decimal point correctly as this re-introduces the float issue either in Go or in browser-side JavaScript. Therefore, when printing you must manipulate the string and manually insert the decimal point at the proper place within that string. You must avoid the float type at all costs at all parts of your stack.
Upvotes: 2
Reputation: 502
Maybe try github.com/shopspring/decimal.
It calls itself a package for "arbitrary-precision fixed-point decimal numbers in go".
NOTE: can "only" represent numbers with a maximum of 2^31 digits after the decimal point.
Upvotes: 2
Reputation: 166588
import "math/big"
func (x *Float) String() string
String formats x like x.Text('g', 10). (String must be called explicitly, Float.Format does not support %s verb.)
Use string input and round the output, for example,
package main
import (
"fmt"
"math/big"
)
func main() {
const prec = 200
a, _ := new(big.Float).SetPrec(prec).SetString("5000")
b, _ := new(big.Float).SetPrec(prec).SetString("4000.30")
result := new(big.Float).Sub(a, b)
fmt.Println(result)
fmt.Println(result.String())
}
Output:
999.6999999999999999999999999999999999999999999999999999999995
999.7
For decimal, by definition, binary floating-point numbers are an approximation. For example, the decimal number 0.1
cannot be represented exactly, it is approximately 1.10011001100110011001101 * (2**(-4))
.
You are already used to this sort of thing since you know about repeating decimals, an approximation for rational numbers: 1 / 3 = .333...
and 3227 / 555 = 5.8144144144...
.
See What Every Computer Scientist Should Know About Floating-Point Arithmetic.
Upvotes: 14