2 minutes
Minimizing Data Bias with Half-to-Even (Banker’s) Rounding in Go
Unwanted Bias from Standard Rounding
When aggregating large volumes of financial or statistical numbers, standard arithmetic rounding (math.Round) introduces a subtle upward drift in total sums. Because math.Round always rounds .5 away from zero (2.5 -> 3, 3.5 -> 4), any dataset with frequent half-way values accumulates a positive bias over thousands of operations.
Half-to-Even (Banker’s) Rounding
To eliminate this directional bias, financial systems typically use Half-to-Even rounding (also called Banker’s Rounding).
Instead of always rounding .5 up, Half-to-Even rounds an exact .5 fraction to the nearest even integer:
2.5rounds down to2(since2is even)3.5rounds up to4(since4is even)
Over a large dataset, half-way values round up and down roughly equally, canceling out cumulative drift.
Implementing It in Go
Since Go 1.10, the standard library’s math package includes math.RoundToEven out of the box, so you don’t need to roll your own modulo/floor logic (which is also easy to get wrong for negative numbers):
package main
import (
"fmt"
"math"
)
// roundCentsToEven rounds a monetary amount to 2 decimal places using Banker's Rounding.
func roundCentsToEven(amount float64) float64 {
return math.RoundToEven(amount*100) / 100
}
func main() {
fmt.Printf("2.5 -> %v\n", math.RoundToEven(2.5)) // 2 (nearest even)
fmt.Printf("3.5 -> %v\n", math.RoundToEven(3.5)) // 4 (nearest even)
fmt.Printf("-2.5 -> %v\n", math.RoundToEven(-2.5)) // -2 (nearest even)
fmt.Printf("2.6 -> %v\n", math.RoundToEven(2.6)) // 3 (standard rounding)
fmt.Printf("2.4 -> %v\n", math.RoundToEven(2.4)) // 2 (standard rounding)
fmt.Printf("10.125 cents -> %.2f\n", roundCentsToEven(10.125)) // 10.12
fmt.Printf("10.135 cents -> %.2f\n", roundCentsToEven(10.135)) // 10.14
}
Note on floating-point precision: If you are dealing with ledger balances or currency conversion in production,
float64binary representation can still turn a decimal like1.005into1.0049999999999998934...beforemath.RoundToEvensees it. For strict decimal arithmetic in Go, prefer integer minor units (cents) or a decimal package likeshopspring/decimal(which uses Banker’s Rounding viaRoundBank).