Skip to content

fix: normalize exponent in rounding functions and support negative precision in Truncate#426

Open
amitmishra11 wants to merge 1 commit into
shopspring:masterfrom
amitmishra11:fix/rounding-exponent-normalization
Open

fix: normalize exponent in rounding functions and support negative precision in Truncate#426
amitmishra11 wants to merge 1 commit into
shopspring:masterfrom
amitmishra11:fix/rounding-exponent-normalization

Conversation

@amitmishra11

Copy link
Copy Markdown

Summary

This PR fixes two related bugs in decimal rounding and truncation:

  1. RoundUp, RoundDown, RoundFloor, and RoundCeil returned the original decimal (with its original exponent) when the value was already exact at the requested precision, causing the result to carry a stale exponent that did not match the requested places. (Fixes Inconsistent rounding behavior in Round and RoundUp methods #390)

  2. Truncate rejected negative precision values due to a precision >= 0 guard, silently returning the input unchanged. Negative precision should truncate the integer part toward zero — for example (5432).Truncate(-2) should return 5400. (Fixes Truncate should parse negative values too, or return error #406)

Changes

decimal.go

  • RoundCeil: return rescaled (not d) when the value is already exact at the target precision
  • RoundFloor: same
  • RoundUp: same
  • RoundDown: same
  • Truncate: remove the precision >= 0 guard; rescale uses big.Int.Quo (truncates toward zero) so negative precision works correctly without additional adjustment; update doc comment and examples

decimal_test.go

  • TestTruncateNegativePrecision: table-driven tests for Truncate with negative and positive precision, checking both value and exponent
  • TestRoundingExponentNormalization: table-driven tests for all four rounding functions verifying that the returned exponent matches the requested places when the value is already exact

Testing

$ go test ./...
ok  github.com/shopspring/decimal  8.2s

Example: exponent normalization (issue #390)

Before this fix:

d, _ := decimal.NewFromString("100.0")
r := d.RoundUp(0)
r.Exponent()                     // -1  (wrong, should be 0)
r.StringFixedBank(-r.Exponent()) // "100.0"  (wrong)

After:

d, _ := decimal.NewFromString("100.0")
r := d.RoundUp(0)
r.Exponent()                     // 0
r.StringFixedBank(-r.Exponent()) // "100"

The same fix applies to RoundDown, RoundFloor, and RoundCeil.

Example: negative precision in Truncate (issue #406)

Before:

decimal.RequireFromString("5432").Truncate(-2).String() // "5432"  (unchanged, wrong)

After:

decimal.RequireFromString("5432").Truncate(-2).String()  // "5400"
decimal.RequireFromString("-5432").Truncate(-2).String() // "-5400"
decimal.RequireFromString("5499").Truncate(-2).String()  // "5400"
decimal.RequireFromString("5500").Truncate(-2).String()  // "5500"

… support negative precision in Truncate

RoundUp, RoundDown, RoundFloor, and RoundCeil all returned the original
decimal (with its original exponent) when the value was already exact at
the requested precision. This caused the result to carry a trailing
exponent that did not match the requested places.

For example, (100.0).RoundUp(0) returned a Decimal with exp=-1 instead
of exp=0, so StringFixedBank(-d.Exponent()) would produce "100.0" rather
than "100".

Fix: when the rescaled value equals the original, return the rescaled
form (which has the canonical exponent) instead of the original.

Truncate previously rejected negative precision values due to a
precision >= 0 guard, silently returning the input unchanged. The
rescale function already uses integer division (Quo, truncating toward
zero), so removing the guard is sufficient to make negative precision
work correctly.

For example, (5432).Truncate(-2) now returns 5400 instead of 5432.

Fixes shopspring#390
Fixes shopspring#406
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Truncate should parse negative values too, or return error Inconsistent rounding behavior in Round and RoundUp methods

1 participant