Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,7 @@ func (d Decimal) PowInt32(exp int32) (Decimal, error) {
}

if isExpNeg {
return New(1, 0).DivRound(result, int32(PowPrecisionNegativeExponent)), nil
return divForNegativePow(result, int32(PowPrecisionNegativeExponent)), nil
}

return result, nil
Expand Down Expand Up @@ -955,12 +955,30 @@ func (d Decimal) powBigIntWithPrecision(exp *big.Int, precision int32) (Decimal,
}

if isExpNeg {
return New(1, 0).DivRound(result, precision), nil
return divForNegativePow(result, precision), nil
}

return result, nil
}

// divForNegativePow computes 1/result with DivRound(precision).
// If the configured precision is too small to represent a non-zero reciprocal
// (e.g. 10**-17 with PowPrecisionNegativeExponent=16), retry with enough digits
// so exact powers of ten are not collapsed to zero (#394).
func divForNegativePow(result Decimal, precision int32) Decimal {
q := New(1, 0).DivRound(result, precision)
if !q.IsZero() || result.IsZero() {
return q
}
needed := int32(result.NumDigits())
if needed < precision {
needed = precision
}
// One extra digit covers borderline powers of ten such as 10**n (n digits of 1
// followed by zeros? 10**17 has 18 digits) — NumDigits already returns 18.
return New(1, 0).DivRound(result, needed)
}

// ExpHullAbrham calculates the natural exponent of decimal (e to the power of d) using Hull-Abraham algorithm.
// OverallPrecision argument specifies the overall precision of the result (integer part + decimal part).
//
Expand Down
27 changes: 27 additions & 0 deletions decimal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2870,6 +2870,33 @@ func TestDecimal_PowInt32(t *testing.T) {
}
}

func TestDecimal_PowNegativeIntegerPowersOfTen(t *testing.T) {
// Regression for #394: default PowPrecisionNegativeExponent (16) used to
// DivRound 1/10**n to zero for n > 16 even though the value is exact.
for _, exp := range []int32{-16, -17, -18, -20} {
base := NewFromInt(10)
got, err := base.PowInt32(exp)
if err != nil {
t.Fatalf("PowInt32(10, %d) unexpected error: %v", exp, err)
}
want := New(1, exp) // 10**(-n) == 1e-n
if !got.Equal(want) {
t.Errorf("PowInt32(10, %d) = %s, want %s", exp, got, want)
}
got2 := base.Pow(NewFromInt(int64(exp)))
if !got2.Equal(want) {
t.Errorf("Pow(10, %d) = %s, want %s", exp, got2, want)
}
got3, err := base.PowBigInt(big.NewInt(int64(exp)))
if err != nil {
t.Fatalf("PowBigInt(10, %d) unexpected error: %v", exp, err)
}
if !got3.Equal(want) {
t.Errorf("PowBigInt(10, %d) = %s, want %s", exp, got3, want)
}
}
}

func TestDecimal_PowInt32_UndefinedResult(t *testing.T) {
base := RequireFromString("0")

Expand Down