mirror of
https://github.com/shopspring/decimal.git
synced 2024-11-22 20:40:48 +01:00
Implemented: normalizing, rounding to significant figures
This commit is contained in:
parent
9aa42f1878
commit
f62241cc93
2 changed files with 153 additions and 0 deletions
54
decimal.go
54
decimal.go
|
@ -910,6 +910,60 @@ func (d Decimal) Truncate(precision int32) Decimal {
|
|||
return d
|
||||
}
|
||||
|
||||
// RoundToSignificantFigures returns a copy of a decimal rounded to specified number of significant figures.
|
||||
// For negative or zero figures the function returns zero.
|
||||
// Result is normalized without trailing zeros.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// NewFromString("5.45").RoundToSignificantFigures(2).String() // output: "5.5"
|
||||
// NewFromString("545").RoundToSignificantFigures(2).String() // output: "550"
|
||||
//
|
||||
func (d Decimal) RoundToSignificantFigures(figures int32) Decimal {
|
||||
if figures <= 0 {
|
||||
return Zero
|
||||
}
|
||||
d = d.Normalize()
|
||||
twoMant := big.NewInt(0).Set(d.value)
|
||||
twoMant.Abs(twoMant)
|
||||
twoMant.Mul(twoMant, twoInt)
|
||||
upper := big.NewInt(int64(figures))
|
||||
upper.Exp(tenInt, upper, nil)
|
||||
upper.Mul(upper, twoInt)
|
||||
upper.Sub(upper, oneInt)
|
||||
m := int64(0)
|
||||
for twoMant.Cmp(upper) >= 0 {
|
||||
upper.Mul(upper, tenInt)
|
||||
m++
|
||||
}
|
||||
if int64(d.exp)+m > int64(math.MaxInt32) {
|
||||
panic(fmt.Sprintf("exponent %d overflows an int32", int64(d.exp)+m))
|
||||
}
|
||||
return d.Round(-d.exp - int32(m)).Normalize()
|
||||
}
|
||||
|
||||
// Normalize normalizes decimal value.
|
||||
// It strips all the trailing zeros.
|
||||
// More specifically if a mantissa is representable as m*10^n it returns a decimal with a mantissa m and its exponent incremented by n.
|
||||
func (d Decimal) Normalize() Decimal {
|
||||
d.ensureInitialized()
|
||||
quo := big.NewInt(0).Set(d.value)
|
||||
prevQuo := big.NewInt(0).Set(d.value)
|
||||
rem := big.NewInt(0)
|
||||
n := int64(0)
|
||||
for {
|
||||
quo.QuoRem(quo, tenInt, rem)
|
||||
if rem.Cmp(zeroInt) != 0 || quo.Cmp(zeroInt) == 0 {
|
||||
if int64(d.exp)+n > int64(math.MaxInt32) {
|
||||
panic(fmt.Sprintf("exponent %d overflows an int32", int64(d.exp)+n))
|
||||
}
|
||||
return Decimal{value: prevQuo, exp: d.exp + int32(n)}
|
||||
}
|
||||
prevQuo.Set(quo)
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (d *Decimal) UnmarshalJSON(decimalBytes []byte) error {
|
||||
if string(decimalBytes) == "null" {
|
||||
|
|
|
@ -2577,6 +2577,105 @@ func TestTan(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNormalize(t *testing.T) {
|
||||
tbl := []struct {
|
||||
inp Decimal
|
||||
res Decimal
|
||||
}{
|
||||
{New(0, 0), New(0, 0)},
|
||||
{New(10, 0), New(1, 1)},
|
||||
{New(100, 0), New(1, 2)},
|
||||
{New(11, 0), New(11, 0)},
|
||||
{New(111, 0), New(111, 0)},
|
||||
{New(-10, 0), New(-1, 1)},
|
||||
{New(-100, 0), New(-1, 2)},
|
||||
{New(-11, 0), New(-11, 0)},
|
||||
{New(-111, 0), New(-111, 0)},
|
||||
{New(0, 5), New(0, 0)},
|
||||
{New(10, 5), New(1, 6)},
|
||||
{New(100, 5), New(1, 7)},
|
||||
{New(11, 5), New(11, 5)},
|
||||
{New(111, 5), New(111, 5)},
|
||||
{New(-10, 5), New(-1, 6)},
|
||||
{New(-100, 5), New(-1, 7)},
|
||||
{New(-11, 5), New(-11, 5)},
|
||||
{New(-111, 5), New(-111, 5)},
|
||||
{New(0, -5), New(0, 0)},
|
||||
{New(10, -5), New(1, -4)},
|
||||
{New(100, -5), New(1, -3)},
|
||||
{New(11, -5), New(11, -5)},
|
||||
{New(111, -5), New(111, -5)},
|
||||
{New(-10, -5), New(-1, -4)},
|
||||
{New(-100, -5), New(-1, -3)},
|
||||
{New(-11, -5), New(-11, -5)},
|
||||
{New(-111, -5), New(-111, -5)},
|
||||
}
|
||||
for _, i := range tbl {
|
||||
if norm := i.inp.Normalize(); norm.Cmp(i.res) != 0 {
|
||||
t.Errorf("unexpected normalization result, expected: %v, got: %v", i.res, norm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundToSignificantFigures(t *testing.T) {
|
||||
type testData struct {
|
||||
inp Decimal
|
||||
fig int32
|
||||
res Decimal
|
||||
}
|
||||
strtbl := []struct {
|
||||
inp string
|
||||
fig int32
|
||||
res string
|
||||
}{
|
||||
{"5.45", 2, "5.5"},
|
||||
{"545", 2, "550"},
|
||||
{"0", 0, "0"},
|
||||
{"0", 1, "0"},
|
||||
{"0", -1, "0"},
|
||||
{"1", 0, "0"},
|
||||
{"1", 1, "1"},
|
||||
{"1", 2, "1"},
|
||||
{"1", -1, "0"},
|
||||
{"10", 1, "10"},
|
||||
{"10", 0, "0"},
|
||||
{"100", 1, "100"},
|
||||
{"110", 1, "100"},
|
||||
{"110", 2, "110"},
|
||||
{"995", 1, "1000"},
|
||||
{"995", 2, "1000"},
|
||||
{"994", 2, "990"},
|
||||
{"995", 3, "995"},
|
||||
{"995", 4, "995"},
|
||||
{"0.995", 1, "1"},
|
||||
{"0.995", 2, "1"},
|
||||
{"0.994", 2, "0.99"},
|
||||
{"0.995", 3, "0.995"},
|
||||
{"0.995", 4, "0.995"},
|
||||
{"994.99999999999999999999999999", 2, "990"},
|
||||
{"994.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 2, "990"},
|
||||
{"995.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 2, "1000"},
|
||||
{"0.101", 1, "0.1"},
|
||||
{"0.101", 2, "0.1"},
|
||||
{"0.101", 3, "0.101"},
|
||||
}
|
||||
tbl := []testData{{Decimal{}, 1, Decimal{}}}
|
||||
for _, x := range strtbl {
|
||||
tbl = append(tbl, testData{RequireFromString(x.inp), x.fig, RequireFromString(x.res)})
|
||||
// Symmetric negative tests
|
||||
if x.inp != "0" {
|
||||
x.inp = "-" + x.inp
|
||||
x.res = "-" + x.res
|
||||
tbl = append(tbl, testData{RequireFromString(x.inp), x.fig, RequireFromString(x.res)})
|
||||
}
|
||||
}
|
||||
for _, x := range tbl {
|
||||
if rnd := x.inp.RoundToSignificantFigures(x.fig); rnd.Cmp(x.res) != 0 {
|
||||
t.Errorf("unexpected rounding to significant figures result, expected: %s, got: %v", x.res, rnd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleNewFromFloat32() {
|
||||
fmt.Println(NewFromFloat32(123.123123123123).String())
|
||||
fmt.Println(NewFromFloat32(.123123123123123).String())
|
||||
|
|
Loading…
Reference in a new issue