Skip to content

Commit

Permalink
Decimal: complete scan() implementation
Browse files Browse the repository at this point in the history
Still need working Quo() and Mul() in order to parse exponents of 2.
  • Loading branch information
db47h committed May 5, 2020
1 parent 34943d7 commit f1654da
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 37 deletions.
66 changes: 44 additions & 22 deletions decimal_conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,9 @@ func (z *Decimal) scan(r io.ByteScanner, base int) (f *Decimal, b int, err error
// 2 and 5. This reduces the size of the multiplication factor
// needed for base-10 exponents.

panic("adjust exponent needed")

// normalize mantissa and determine initial exponent contributions
// exp2 := int64(len(z.mant))*_W - fnorm(z.mant)
exp2 := int64(0)
exp5 := int64(0)
exp10 := int64(len(z.mant))*_WD - dnorm(z.mant)

// determine binary or decimal exponent contribution of radix point
if fcount < 0 {
Expand All @@ -90,8 +87,7 @@ func (z *Decimal) scan(r io.ByteScanner, base int) (f *Decimal, b int, err error
d := int64(fcount)
switch b {
case 10:
exp5 = d
fallthrough // 10**e == 5**e * 2**e
exp10 = d
case 2:
exp2 += d
case 8:
Expand All @@ -107,44 +103,70 @@ func (z *Decimal) scan(r io.ByteScanner, base int) (f *Decimal, b int, err error
// take actual exponent into account
switch ebase {
case 10:
exp5 += exp
fallthrough // see fallthrough above
exp10 += exp
case 2:
exp2 += exp
default:
panic("unexpected exponent base")
}
// exp consumed - not needed anymore
// exp consumed - not needed anymoregri

// apply 2**exp2
if MinExp <= exp2 && exp2 <= MaxExp {
// apply 10**exp10
if MinExp <= exp10 && exp10 <= MaxExp {
z.prec = prec
z.form = finite
z.exp = int32(exp2)
z.exp = int32(exp10)
f = z
} else {
err = fmt.Errorf("exponent overflow")
return
}

if exp5 == 0 {
// no decimal exponent contribution
if exp2 == 0 {
// no binary exponent contribution
z.round(0)
return
}
// exp5 != 0
// exp2 != 0

// // apply 5**exp5
// p := new(Decimal).SetPrec(z.Prec() + _WD) // use more bits for p -- TODO(gri) what is the right number?
// if exp5 < 0 {
// z.Quo(z, p.pow5(uint64(-exp5)))
// } else {
// z.Mul(z, p.pow5(uint64(exp5)))
// }
// // apply 2**exp2
p := new(Decimal).SetPrec(z.Prec() + _WD) // use more bits for p -- TODO(db47h) what is the right number?
if exp2 < 0 {
z.Quo(z, p.pow2(uint64(-exp2)))
} else {
z.Mul(z, p.pow2(uint64(exp2)))
}

return
}

// pow5 sets z to 5**n and returns z.
// n must not be negative.
func (z *Decimal) pow2(n uint64) *Decimal {
const m = _WD * 100000 / 30103 // maximum exponent such that 2**m < _BD
if n <= m {
return z.SetUint64(1 << n)
}
// n > m

z.SetUint64(1 << m)
n -= m

// use more bits for f than for z
// TODO(db47h) what is the right number?
f := new(Decimal).SetPrec(z.Prec() + _WD).SetUint64(2)

for n > 0 {
if n&1 != 0 {
z.Mul(z, f)
}
f.Mul(f, f)
n >>= 1
}

return z
}

// Parse parses s which must contain a text representation of a floating- point
// number with a mantissa in the given conversion base (the exponent is always a
// decimal number), or a string representing an infinite value.
Expand Down
30 changes: 15 additions & 15 deletions decimal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var intData = []struct {
e int32
}{
{"00000000000000000001232", 10, 0, dec{1232000000000000000}, 19, 4},
{"1234567890123456789_0123456789012345678_9012345678901234567_8901234567890123456_78901234567890", 0, 0,
{"1234567890123456789_0123456789012345678_9012345678901234567_8901234567890123456_78901234567890", 0, 90,
dec{7890123456789000000, 8901234567890123456, 9012345678901234567, 123456789012345678, 1234567890123456789},
90, 90},
{"1235", 0, 0, dec{1235000000000000000}, _WD, 4},
Expand Down Expand Up @@ -76,20 +76,20 @@ func TestDecimal_SetInt(t *testing.T) {
}

func TestDecimal_SetString(t *testing.T) {
// for i, td := range intData {
// t.Run(strconv.Itoa(i), func(t *testing.T) {
// d, _ := new(Decimal).SetMode(ToNearestEven).SetPrec(td.p).SetString(td.s)
// if !reflect.DeepEqual(td.d, d.mant) {
// t.Fatalf("\nexpected mantissa %v\n got %v", td.d, d.mant)
// }
// if td.pr != d.Prec() {
// t.Fatalf("\nexpected precision %v\n got %v", td.pr, d.Prec())
// }
// if td.e != d.exp {
// t.Fatalf("\nexpected exponent %v\n got %v", td.p, d.Prec())
// }
// })
// }
for i, td := range intData {
t.Run(strconv.Itoa(i), func(t *testing.T) {
d, _ := new(Decimal).SetMode(ToNearestEven).SetPrec(td.p).SetString(td.s)
if !reflect.DeepEqual(td.d, d.mant) {
t.Fatalf("\nexpected mantissa %v\n got %v", td.d, d.mant)
}
if td.pr != d.Prec() {
t.Fatalf("\nexpected precision %v\n got %v", td.pr, d.Prec())
}
if td.e != d.exp {
t.Fatalf("\nexpected exponent %v\n got %v", td.p, d.Prec())
}
})
}
}

func BenchmarkDnorm(b *testing.B) {
Expand Down

0 comments on commit f1654da

Please sign in to comment.