Skip to content

Commit

Permalink
Change NewDecimal API
Browse files Browse the repository at this point in the history
  • Loading branch information
db47h committed May 23, 2020
1 parent 9eba24f commit 06c3b88
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 32 deletions.
9 changes: 4 additions & 5 deletions dec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
40 changes: 22 additions & 18 deletions decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
}
4 changes: 2 additions & 2 deletions decimal_sqrt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions decimal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down
8 changes: 4 additions & 4 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 06c3b88

Please sign in to comment.