From ccc7cc2931b8f0b3df41e1f5c1e7322d708b6623 Mon Sep 17 00:00:00 2001 From: Oleg Lomaka Date: Tue, 4 Jan 2022 16:25:19 +0200 Subject: [PATCH] Assign Numeric to *big.Rat --- numeric.go | 26 ++++++++++++++++++++++++++ numeric_test.go | 13 +++++++++++++ 2 files changed, 39 insertions(+) diff --git a/numeric.go b/numeric.go index a939625b..cd057749 100644 --- a/numeric.go +++ b/numeric.go @@ -369,6 +369,12 @@ func (src *Numeric) AssignTo(dst interface{}) error { return fmt.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) } *v = normalizedInt.Uint64() + case *big.Rat: + rat, err := src.toBigRat() + if err != nil { + return err + } + v.Set(rat) default: if nextDst, retry := GetAssignToDstType(dst); retry { return src.AssignTo(nextDst) @@ -406,6 +412,26 @@ func (dst *Numeric) toBigInt() (*big.Int, error) { return num, nil } +func (dst *Numeric) toBigRat() (*big.Rat, error) { + if dst.NaN { + return nil, fmt.Errorf("%v is not a number", dst) + } else if dst.InfinityModifier == Infinity { + return nil, fmt.Errorf("%v is infinity", dst) + } else if dst.InfinityModifier == NegativeInfinity { + return nil, fmt.Errorf("%v is -infinity", dst) + } + + num := new(big.Rat).SetInt(dst.Int) + if dst.Exp > 0 { + mul := new(big.Int).Exp(big10, big.NewInt(int64(dst.Exp)), nil) + num.Mul(num, new(big.Rat).SetInt(mul)) + } else if dst.Exp < 0 { + mul := new(big.Int).Exp(big10, big.NewInt(int64(-dst.Exp)), nil) + num.Quo(num, new(big.Rat).SetInt(mul)) + } + return num, nil +} + func (src *Numeric) toFloat64() (float64, error) { if src.NaN { return math.NaN(), nil diff --git a/numeric_test.go b/numeric_test.go index 455c3ac3..83334a04 100644 --- a/numeric_test.go +++ b/numeric_test.go @@ -263,6 +263,7 @@ func TestNumericAssignTo(t *testing.T) { var f64 float64 var pf32 *float32 var pf64 *float64 + var br = new(big.Rat) simpleTests := []struct { src *pgtype.Numeric @@ -293,6 +294,9 @@ func TestNumericAssignTo(t *testing.T) { {src: &pgtype.Numeric{Status: pgtype.Present, InfinityModifier: pgtype.Infinity}, dst: &f32, expected: float32(math.Inf(1))}, {src: &pgtype.Numeric{Status: pgtype.Present, InfinityModifier: pgtype.NegativeInfinity}, dst: &f64, expected: math.Inf(-1)}, {src: &pgtype.Numeric{Status: pgtype.Present, InfinityModifier: pgtype.NegativeInfinity}, dst: &f32, expected: float32(math.Inf(-1))}, + {src: &pgtype.Numeric{Int: big.NewInt(-1023), Exp: -2, Status: pgtype.Present}, dst: br, expected: big.NewRat(-1023, 100)}, + {src: &pgtype.Numeric{Int: big.NewInt(-1023), Exp: 2, Status: pgtype.Present}, dst: br, expected: big.NewRat(-102300, 1)}, + {src: &pgtype.Numeric{Int: big.NewInt(23), Exp: 0, Status: pgtype.Present}, dst: br, expected: big.NewRat(23, 1)}, } for i, tt := range simpleTests { @@ -317,6 +321,11 @@ func TestNumericAssignTo(t *testing.T) { } else if !nanExpected && dst != tt.expected { t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) } + case big.Rat: + if (&dstTyped).Cmp(tt.expected.(*big.Rat)) != 0 { + t.Errorf("%d: expected %v to assign %v, but result was %v", + i, tt.src, tt.expected, dst) + } default: if dst != tt.expected { t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) @@ -356,6 +365,10 @@ func TestNumericAssignTo(t *testing.T) { {src: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}, dst: &ui64}, {src: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}, dst: &ui}, {src: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Null}, dst: &i32}, + {src: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Null}, dst: br}, + {src: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Present, NaN: true}, dst: br}, + {src: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Present, InfinityModifier: pgtype.Infinity}, dst: br}, + {src: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Present, InfinityModifier: pgtype.NegativeInfinity}, dst: br}, } for i, tt := range errorTests {