What is the best way to convert a currency from float to integer in Go?
------------- Added Explanation of question ----------
To expand my question a little, the following is an example of what I see as the “problem” solved by adding a rounding value of 0.004 (for 2-decimal currencies).
As far as I know, external values stored as eg. decimal, double, currency in an RDBMS need to be “imported” to Go as a float. In order to perform calculations, they then need to be converted to integer, or at least that is one method.
In the example below, fVal1 emulates an amount imported from a database. To perform calculations on it, I want to convert it to an integer. The easiest way to do that appears to me to be to add a rounding figure as illustrated.
Example code:
var fVal1 float64 = 19.08
var f100 float64 = 100.0
var fRound float64 = 0.004
var fResult float64 = fVal1 * f100
fmt.Printf("%f * %f as float = %f
", fVal1, f100, fResult)
var iResult1 int64 = int64(fResult)
fmt.Printf("as Integer unrounded = %d
", iResult1)
var iResult2 int64 = int64(fResult + fRound)
fmt.Printf("as Integer rounded = %d
", iResult2)
Console output:
19.080000 * 100.000000 as float = 1908.000000
as Integer unrounded = 1907
as Integer rounded = 1908
----------- end of addition to question -----------
I’ve implemented a small package to handle multiple currencies in Go, and essentially it revolves around the following bit of code (below).
When I discovered that there were rounding errors in converting between floats and integers, the first rounding factor that I tried was 0.004 (for 2 decimals), and it appeared to work OK.
Basically, the following bit of code for currency conversion from float to int revolves around these two alternative lines of code :
var iCcyAmt1 int64 = int64(fCcyBase * fScale)
var iCcyAmt2 int64 = int64((fCcyBase + fRound) * fScale)
The rounding being used in “fRound” in the second alternative is “0.004”.
Running this test program (10 million iterations) results in a consistent rounding error of about 588,000 cents where the “0.004” is not added, and zero difference using the rounding figure.
Is this a safe way to handle this situation (I don’t want to use strings), or is there a better way?
Test Program:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var (
fRound float64 = 0.004
fScale float64 = 100.0
iCcyTotBase int64
iCcyTot1 int64
iCcyTot2 int64
)
const I_ITERS int = 10000000
rand.Seed(time.Now().UTC().UnixNano())
fmt.Printf("
Testing Float to Integer (%d iterations)"+
" .........", I_ITERS)
for i := 0; i < I_ITERS; i++ {
var iCcyBase int64 = int64(999 + rand.Intn(9999999))
var fCcyBase float64 = float64(iCcyBase) / fScale
var iCcyAmt1 int64 = int64(fCcyBase * fScale)
var iCcyAmt2 int64 = int64((fCcyBase + fRound) * fScale)
iCcyTotBase += iCcyBase
iCcyTot1 += iCcyAmt1
iCcyTot2 += iCcyAmt2
}
var iCcyDiff1 int64 = iCcyTot1 - iCcyTotBase
var iCcyDiff2 int64 = iCcyTot2 - iCcyTotBase
fmt.Printf("
Diff without rounding = %d
", iCcyDiff1)
fmt.Printf("Diff with rounding = %d
", iCcyDiff2)
}