Skip to content

Correctly round Double(PseudoDecimal) - #83

Open
deadcaf3 wants to merge 1 commit into
apple:mainfrom
deadcaf3:fix-double-pseudodecimal-rounding
Open

deadcaf3 wants to merge 1 commit into
apple:mainfrom
deadcaf3:fix-double-pseudodecimal-rounding

Conversation

@deadcaf3

Copy link
Copy Markdown

Motivation:

Double(PseudoDecimal) computes mantissa * pow(10, exponent). For negative exponents, values like pow(10, -1) aren't exactly representable, so the result is rounded twice and often lands one ULP off. Decoding 0.3 as a Double gives 0.30000000000000004, and -19.993 gives -19.993000000000002.

Modifications:

  • Divide by pow(10, -exponent) instead. 10, 100 and 1000 are exact, and the mantissa always fits in a Double's 53 bits, so there's only a single rounding step.
  • Add a decoder test for both values above.

Result:

Decimals convert to the nearest Double. As a sanity check I compared Double(decimal) with Double(String(decimal)): before the fix about 16% of a sweep of mantissas between -20,000 and 20,000 disagreed, and after it a wider sweep of 1.6 million values had no mismatches.

Tested locally on macOS with Swift 6.4.1, swift test passes.

Motivation:

Double(PseudoDecimal) multiplied the mantissa by pow(10, exponent). For
negative exponents that power of ten is not exactly representable, so the
result is rounded twice and can be off by one ULP. Decoding "0.3" as a
Double produced 0.30000000000000004.

Modifications:

Divide the mantissa by the exactly representable power of ten instead,
so the result is rounded once. Add a decoder test covering the case.

Result:

Double(PseudoDecimal) returns the nearest Double to the decimal value.
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.

1 participant