Rounding positive value (example here: 1.015) half-up to 2 decimal places using math.Round()
in Go:
fmt.Println(math.Round(1.015*100) / 100)
Go Playground
I got: 1.02
. That's correct.
But when I employed a function to do the same job:
func RoundHalfUp(x float64) float64 {
return math.Round(x*100) / 100
}
Go Playground
I got 1.01
.
What's wrong with the RoundHalfUp
function?
The Go Programming Language Specification
Constants
Numeric constants represent exact values of arbitrary precision and do not overflow.
Implementation restriction: Although numeric constants have arbitrary precision in the language, a compiler may implement them using an internal representation with limited precision. That said, every implementation must:
- Represent floating-point constants, including the parts of a complex constant, with a mantissa of at least 256 bits and a signed
binary exponent of at least 16 bits.- Round to the nearest representable constant if unable to represent a floating-point or complex constant due to limits on precision.
These requirements apply both to literal constants and to the result of evaluating constant expressions.
Constant expressions
Constant expressions may contain only constant operands and are evaluated at compile time.
Constant expressions are always evaluated exactly; intermediate values and the constants themselves may require precision significantly larger than supported by any predeclared type in the language.
Implementation restriction: A compiler may use rounding while computing untyped floating-point or complex constant expressions; see the implementation restriction in the section on constants. This rounding may cause a floating-point constant expression to be invalid in an integer context, even if it would be integral when calculated using infinite precision, and vice versa.
Implement the RoundHalfUp
function like the Go compiler does for math.Round(1.015*100) / 100
. 1.015*100
is a untyped floating-point constant expression. Use the math/big
package with at least 256 bits of precision. Go float64
(IEEE-754 64-bit floating-point) has 53 bits of precision.
For example, with 256 bits of precision (constant expression),
package main
import (
"fmt"
"math"
"math/big"
)
func RoundHalfUp(x string) float64 {
// math.Round(x*100) / 100
xf, _, err := big.ParseFloat(x, 10, 256, big.ToNearestEven)
if err != nil {
panic(err)
}
xf100, _ := new(big.Float).Mul(xf, big.NewFloat(100)).Float64()
return math.Round(xf100) / float64(100)
}
func main() {
fmt.Println(RoundHalfUp("1.015"))
}
Playground: https://play.golang.org/p/uqtYwP4o22B
Output:
1.02
If we only use 53 bits of precision (float64
):
xf, _, err := big.ParseFloat(x, 10, 53, big.ToNearestEven)
Playground: https://play.golang.org/p/ejz-wkuycaU
Output:
1.01
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With