Why is this usage of math/big.Float.SetString no bug?

The following prints false:

package main

import (
	"fmt"
	"math/big"
)

func main() {
	r1 := big.NewRat(1, 10)
	f1 := big.NewFloat(0.1)
	q1 := new(big.Float).SetPrec(f1.Prec() + 1).SetRat(r1)
	c1 := q1.Cmp(f1)
	fmt.Println(c1)

	r2, ok := new(big.Rat).SetString("1/10")
	if !ok {
		panic("1/10")
	}
	f2, ok := new(big.Float).SetString("0.1")
	if !ok {
		panic("0.1")
	}
	q2 := new(big.Float).SetPrec(f2.Prec() + 1).SetRat(r2)
	c2 := q2.Cmp(f2)
	fmt.Println(c2)

	fmt.Println(c1 == c2)
}


Why is it no bug?

I think the rational number 1/10 should be always less than the floating point number 0.1.

The rational number 1/10 exactly represents the mathematical number of one divided by ten. All floating point numbers are binary representations of a number, which is as close as possible to 1/10

The binary representation of 1/10 is periodic: 0.0 0011 0011 0011 0011 0011 ....

By setting the precision for a float you essentially decide where to cut off and round this infinitely long number. You will never get an exact representation of 1/10 because that would take a precision of infinity - rather you will get a number which is a bit smaller or a bit bigger, depending on the exact precision used. If you cut after a 0 the result will always be rounded down (simply truncated) and is smaller than 1/10. But if you cut after a 1 bit, then depending on the following bits the number might be rounded up (increased by 1 after truncating) and result in a number bigger than 1/10

You can try this by setting the precision to a manual value and try different precisions

1 Like