Convert floats to int64.

Following the example of bitcoind and the bitcoin wiki, provide a
function to convert floats (which are returned by the json-rpc server)
to int64 (which is the normal representation for most values involving
bitcoins).
This commit is contained in:
John C. Vernaleo 2013-06-26 16:34:45 -04:00
parent 39cef5e76a
commit 86f848ddd4
3 changed files with 67 additions and 2 deletions

View file

@ -742,3 +742,36 @@ func IsValidIdType(id interface{}) bool {
return false return false
} }
} }
// JSONToAmount Safely converts a floating point value to an int.
// Clearly not all floating point numbers can be converted to ints (there
// is no one-to-one mapping, but bitcoin's json api returns most numbers as
// floats which are not safe to use when handling money. Since bitcoins can
// only be divided in a limited way, this methods works for the amounts returned
// by the json api. It is not for general use.
// This follows the method described at:
// https://en.bitcoin.it/wiki/Proper_Money_Handling_%28JSON-RPC%29
func JSONToAmount(jsonAmount float64) (int64, error) {
var amount int64
var err error
if jsonAmount > 1.797693134862315708145274237317043567981e+300 {
err := fmt.Errorf("Error %d is too large to convert", jsonAmount)
return amount, err
}
if jsonAmount < -1.797693134862315708145274237317043567981e+300 {
err := fmt.Errorf("Error %d is too small to convert", jsonAmount)
return amount, err
}
tempVal := 1e8 * jsonAmount
// So we round properly. float won't == 0 and if it did, that
// would be converted fine anyway.
if tempVal < 0 {
tempVal = tempVal - 0.5
}
if tempVal > 0 {
tempVal = tempVal + 0.5
}
// Then just rely on the integer truncating
amount = int64(tempVal)
return amount, err
}

View file

@ -256,7 +256,7 @@ var idtests = []struct {
} }
// TestIsValidIdType tests that IsValidIdType allows (and disallows the correct // TestIsValidIdType tests that IsValidIdType allows (and disallows the correct
// types. // types).
func TestIsValidIdType(t *testing.T) { func TestIsValidIdType(t *testing.T) {
for _, tt := range idtests { for _, tt := range idtests {
res := btcjson.IsValidIdType(tt.testId[0]) res := btcjson.IsValidIdType(tt.testId[0])
@ -266,3 +266,34 @@ func TestIsValidIdType(t *testing.T) {
} }
return return
} }
var floattests = []struct {
in float64
out int64
pass bool
}{
{1.0, 100000000, true},
{-1.0, -100000000, true},
{0.0, 0, true},
{0.00000001, 1, true},
{-0.00000001, -1, true},
{-1.0e307, 0, false},
{1.0e307, 0, false},
}
// TestJSONtoAmount tests that JSONtoAmount returns the proper values.
func TestJSONtoAmount(t *testing.T) {
for _, tt := range floattests {
res, err := btcjson.JSONToAmount(tt.in)
if tt.pass {
if res != tt.out || err != nil {
t.Errorf("Should not fail: ", tt.in)
}
} else {
if err == nil {
t.Errorf("Should not pass: ", tt.in)
}
}
}
return
}

View file

@ -1,5 +1,6 @@
github.com/conformal/btcjson/jsonapi.go CreateMessage 100.00% (310/310) github.com/conformal/btcjson/jsonapi.go CreateMessage 100.00% (310/310)
github.com/conformal/btcjson/jsonapi.go JSONToAmount 100.00% (15/15)
github.com/conformal/btcjson/jsonfxns.go jsonRpcSend 100.00% (7/7) github.com/conformal/btcjson/jsonfxns.go jsonRpcSend 100.00% (7/7)
github.com/conformal/btcjson/jsonfxns.go MarshallAndSend 100.00% (7/7) github.com/conformal/btcjson/jsonfxns.go MarshallAndSend 100.00% (7/7)
github.com/conformal/btcjson/jsonfxns.go GetRaw 100.00% (6/6) github.com/conformal/btcjson/jsonfxns.go GetRaw 100.00% (6/6)
@ -7,5 +8,5 @@ github.com/conformal/btcjson/jsonapi.go jsonWithArgs 100.00% (5/5)
github.com/conformal/btcjson/jsonapi.go IsValidIdType 100.00% (3/3) github.com/conformal/btcjson/jsonapi.go IsValidIdType 100.00% (3/3)
github.com/conformal/btcjson/jsonapi.go RpcCommand 66.67% (18/27) github.com/conformal/btcjson/jsonapi.go RpcCommand 66.67% (18/27)
github.com/conformal/btcjson/jsonapi.go readResultCmd 40.00% (20/50) github.com/conformal/btcjson/jsonapi.go readResultCmd 40.00% (20/50)
github.com/conformal/btcjson --------------- 90.60% (376/415) github.com/conformal/btcjson --------------- 90.93% (391/430)