From 06c3b88bdbb58577c71ce356ccfd1223d3651586 Mon Sep 17 00:00:00 2001 From: Denis Bernard Date: Sat, 23 May 2020 03:29:41 +0200 Subject: [PATCH] Change NewDecimal API --- dec.go | 9 ++++----- decimal.go | 40 ++++++++++++++++++++++------------------ decimal_sqrt.go | 4 ++-- decimal_test.go | 6 +++--- doc.go | 8 ++++---- 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/dec.go b/dec.go index 829f2c4..a6c1269 100644 --- a/dec.go +++ b/dec.go @@ -114,19 +114,18 @@ func (z dec) setWord(x Word) dec { return z } -func (z dec) setUint64(x uint64) (dec, int32) { - dig := int32(decDigits64(x)) +func (z dec) setUint64(x uint64) dec { if w := Word(x); uint64(w) == x && w < _DB { - return z.setWord(w), dig + return z.setWord(w) } // x could be a 2 to 3 words value - z = z.make(int(dig+_DW-1) / _DW) + z = z.make(int(decDigits64(x)+_DW-1) / _DW) for i := 0; i < len(z); i++ { hi, lo := bits.Div64(0, x, _DB) z[i] = Word(lo) x = hi } - return z.norm(), dig + return z.norm() } // toUint64 returns the low 64 bits of z or MaxUint64 and true if z <= MaxUint64. diff --git a/decimal.go b/decimal.go index e2ecfa4..1473c2d 100644 --- a/decimal.go +++ b/decimal.go @@ -91,14 +91,17 @@ type Decimal struct { neg bool } -// NewDecimal allocates and returns a new Decimal set to x, with precision -// 17 and rounding mode ToNearestEven. NewDecimal panics with -// ErrNaN if x is a NaN. -func NewDecimal(x float64) *Decimal { - if math.IsNaN(x) { - panic(ErrNaN{"NewDecimal(NaN)"}) +// NewDecimal allocates and returns a new Decimal set to x×10**exp, with +// precision DefaultDecimalPrec and rounding mode ToNearestEven. +// +// The result will be set to ±0 if x < 0.1×10**MinPexp, or ±Inf if +// x >= 1×10**MaxExp. +func NewDecimal(x int64, exp int) *Decimal { + u := x + if u < 0 { + u = -u } - return new(Decimal).SetFloat64(x) + return new(Decimal).setBits64(x < 0, uint64(u), int64(exp)) } // Abs sets z to the (possibly rounded) value |x| (the absolute value of x) @@ -959,7 +962,7 @@ func (x *Decimal) Rat(z *big.Rat) (*big.Rat, Accuracy) { // z already in normal form case x.exp < allDigits: z.Num().SetBits(decToNat(z.Num().Bits(), x.mant)) - t, _ := dec(nil).setUint64(1) + t := dec(nil).setUint64(1) t = t.shl(t, uint(allDigits-x.exp)) // we cannot set z.b directly since z.norm() is not exported. y := new(big.Rat) @@ -1087,8 +1090,8 @@ func (z *Decimal) SetFloat64(x float64) *Decimal { z.form = finite fmant, exp2 := math.Frexp(x) // get normalized mantissa exp2 -= 64 - z.mant, z.exp = z.mant.setUint64(1<<63 | math.Float64bits(fmant)<<11) - dnorm(z.mant) + z.mant = z.mant.setUint64(1<<63 | math.Float64bits(fmant)<<11) + z.exp = int32(len(z.mant))*_DW - int32(dnorm(z.mant)) if exp2 != 0 { // multiply / divide by 2**exp with increased precision z.prec += 1 @@ -1145,7 +1148,7 @@ func (z *Decimal) SetInt(x *big.Int) *Decimal { return z } -func (z *Decimal) setBits64(neg bool, x uint64) *Decimal { +func (z *Decimal) setBits64(neg bool, x uint64, exp int64) *Decimal { if z.prec == 0 { z.prec = DefaultDecimalPrec } @@ -1157,11 +1160,8 @@ func (z *Decimal) setBits64(neg bool, x uint64) *Decimal { } // x != 0 z.form = finite - z.mant, z.exp = z.mant.setUint64(x) - dnorm(z.mant) - if z.prec < 20 { - z.round(0) - } + z.mant = z.mant.setUint64(x) + z.setExpAndRound(exp+int64(len(z.mant))*_DW-dnorm(z.mant), 0) return z } @@ -1175,7 +1175,7 @@ func (z *Decimal) SetInt64(x int64) *Decimal { } // We cannot simply call z.SetUint64(uint64(u)) and change // the sign afterwards because the sign affects rounding. - return z.setBits64(x < 0, uint64(u)) + return z.setBits64(x < 0, uint64(u), 0) } func (z *Decimal) setExpAndRound(exp int64, sbit uint) { @@ -1286,7 +1286,7 @@ func (z *Decimal) SetRat(x *big.Rat) *Decimal { // precision is 0, it is changed to DefaultDecimalPrec (and rounding will have // no effect). func (z *Decimal) SetUint64(x uint64) *Decimal { - return z.setBits64(false, x) + return z.setBits64(false, x, 0) } // Sign returns: @@ -1642,3 +1642,7 @@ func (z *Decimal) SetBitsExp(mant []Word, exp int64) *Decimal { } return z } + +func (x *Decimal) mantDigits() int64 { + return int64(len(x.mant)) * _DW +} diff --git a/decimal_sqrt.go b/decimal_sqrt.go index 18978ed..03321b2 100644 --- a/decimal_sqrt.go +++ b/decimal_sqrt.go @@ -14,8 +14,8 @@ import ( // license that can be found in the LICENSE file. var ( - oneHalf = NewDecimal(0.5) // must be exact - three = NewDecimal(3.0) // must be exact + oneHalf = NewDecimal(5, -1) + three = NewDecimal(3, 0) ) // Sqrt sets z to the rounded square root of x, and returns it. diff --git a/decimal_test.go b/decimal_test.go index 9b0992f..7d22d8b 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -128,9 +128,9 @@ func BenchmarkDecimal_Float(b *testing.B) { } func TestDecimal_FMA(t *testing.T) { - x := NewDecimal(1.23).SetPrec(3) - y := NewDecimal(2.27).SetPrec(3) - u := NewDecimal(0.003).SetPrec(3) + x := NewDecimal(123, -2).SetPrec(3) + y := NewDecimal(227, -2).SetPrec(3) + u := NewDecimal(3, -3).SetPrec(3) z := new(Decimal).SetPrec(3).Mul(x, y) // == 2.7921 z.Add(z, u) if s := z.String(); s != "2.79" { diff --git a/doc.go b/doc.go index 5afd37e..f8cbf41 100644 --- a/doc.go +++ b/doc.go @@ -37,12 +37,12 @@ in the usual ways and denote 0 without further initialization: Alternatively, new Decimal values can be allocated and initialized with the function: - func NewDecimal(f float64) *Decimal + func NewDecimal(x int64, exp int) *Decimal -For instance, NewDecimal(x) returns a *Decimal set to the value of the float64 -argument f. More flexibility is provided with explicit setters, for instance: +NewDecimal(x, exp) returns a *Decimal set to the value of x×10**exp. More +flexibility is provided with explicit setters, for instance: - z := new(Float).SetUint64(123) // z3 := 123.0 + z := new(Decimal).SetUint64(123) // z3 := 123.0 Setters, numeric operations and predicates are represented as methods of the form: