Skip to content

Commit

Permalink
Merge pull request #220 from shollyman/fr-large-decimals
Browse files Browse the repository at this point in the history
feat: support larger decimals
  • Loading branch information
Karrick McDermott committed Nov 20, 2020
2 parents bca45ff + 76d2559 commit 9f4e906
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 12 deletions.
18 changes: 6 additions & 12 deletions logical_type.go
Expand Up @@ -12,7 +12,6 @@ package goavro
import (
"errors"
"fmt"
"math"
"math/big"
"time"
)
Expand Down Expand Up @@ -285,13 +284,10 @@ func nativeFromDecimalBytes(fn toNativeFn, precision, scale int) toNativeFn {
if !ok {
return nil, bytes, fmt.Errorf("cannot transform to native decimal, expected []byte, received %T", d)
}
i := big.NewInt(0)
fromSignedBytes(i, bs)
if i.BitLen() > 64 {
// Avro spec specifies we return underlying type if the logicalType is invalid
return d, b, err
}
r := big.NewRat(i.Int64(), int64(math.Pow10(scale)))
num := big.NewInt(0)
fromSignedBytes(num, bs)
denom := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(scale)), nil)
r := new(big.Rat).SetFrac(num, denom)
return r, b, nil
}
}
Expand All @@ -302,12 +298,10 @@ func decimalBytesFromNative(fromNativeFn fromNativeFn, toBytesFn toBytesFn, prec
if !ok {
return nil, fmt.Errorf("cannot transform to bytes, expected *big.Rat, received %T", d)
}
// we reduce accuracy to precision by dividing and multiplying by digit length
// Reduce accuracy to precision by dividing and multiplying by digit length
num := big.NewInt(0).Set(r.Num())
denom := big.NewInt(0).Set(r.Denom())

// we get the scaled decimal representation
i := new(big.Int).Mul(num, big.NewInt(int64(math.Pow10(scale))))
i := new(big.Int).Mul(num, new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(scale)), nil))
// divide that by the denominator
precnum := new(big.Int).Div(i, denom)
bout, err := toBytesFn(precnum)
Expand Down
7 changes: 7 additions & 0 deletions logical_type_test.go
Expand Up @@ -144,6 +144,13 @@ func TestDecimalBytesLogicalTypeEncode(t *testing.T) {
testBinaryCodecPass(t, schema, big.NewRat(617, 50), []byte("\x04\x04\xd2"))
testBinaryCodecPass(t, schema, big.NewRat(-617, 50), []byte("\x04\xfb\x2e"))
testBinaryCodecPass(t, schema, big.NewRat(0, 1), []byte("\x02\x00"))
// Test with a large decimal of precision 77 and scale 38
largeDecimalSchema := `{"type": "bytes", "logicalType": "decimal", "precision": 77, "scale": 38}`
n, _ := new(big.Int).SetString("12345678901234567890123456789012345678911111111111111111111111111111111111111", 10)
d, _ := new(big.Int).SetString("100000000000000000000000000000000000000", 10)
largeRat := new(big.Rat).SetFrac(n, d)
testBinaryCodecPass(t, largeDecimalSchema, largeRat, []byte("\x40\x1b\x4b\x68\x19\x26\x11\xfa\xea\x20\x8f\xca\x21\x62\x7b\xe9\xda\xee\x32\x19\x83\x83\x95\x5d\xe8\x13\x1f\x4b\xf1\xc7\x1c\x71\xc7"))

}

func TestDecimalFixedLogicalTypeEncode(t *testing.T) {
Expand Down

0 comments on commit 9f4e906

Please sign in to comment.