proposal-decimal icon indicating copy to clipboard operation
proposal-decimal copied to clipboard

How should the BigDecimal constructor round Numbers?

Open littledan opened this issue 5 years ago • 7 comments

A couple options, suggested by Fabrice Bellard:

(1) BigDecimal(0.1) = 0.1d (i.e. use Number.prototype.toString() and then convert the string to bigdecimal).

(2) BigDecimal(0.1) = 0.1000000000000000055511151231257827021181583404541015625d

(2) is mathematically correct but may be surprising, so I selected (1).

I have to say, I was really expecting (2), but (1) may be more practical. One way to think of a Number is an exact quantity, and another way is to think of it as a range within the ulp.

littledan avatar Jan 08 '20 18:01 littledan

I'd definitely expect (1); i'd be surprised if most people found the actual Number being represented to be an intuitive result.

ljharb avatar Jan 08 '20 18:01 ljharb

Hi!

(2) looks better to me.

and there is two questions, actually:

  1. The default scale for Numbers.
  2. The default rounding mode.

Take a look: https://repl.it/repls/ApprehensiveRobustAssignment

drsm avatar Jan 09 '20 00:01 drsm

It can take a string like in Java

(1) BigDecimal(‘0.1’) = 0.1d

Second variant is finite for Decimal128, but for BigDecimal it’s infinite expression, how it should be stored? Seems that second variant impossible.

0.1000000000000000055511151231257827021181583404541015625…

Also here example with BigInt:

let x = BigDecimal(14677443357n.toString());

munrocket avatar Sep 12 '21 13:09 munrocket

@munrocket the second variant is finite, 0.1).toFixed(100) === 0.1000000000000000055511151231257827021181583404541015625000000000000000000000000000000000000000000000 (as the number can be represented as 2*, you can always multiply by power of 10 (to avoid negative exponent))

Yaffle avatar Oct 04 '21 11:10 Yaffle

Another options is to allow BigDecimal.round to accept numbers and strings:

BigDecimal.round(0.1, {maximumSignificantDigits: 15, roundingMode: 'half-up'}); // 0.1d BigDecimal.round(0.1, {maximumSignificantDigits: 1074, roundingMode: 'half-up'}) // 0.1000000000000000055511151231257827021181583404541015625d

Yaffle avatar Oct 04 '21 11:10 Yaffle

Yea :) I am tried to find this issue when I understand that was wrong, but lost in this repo. So, I just waited when someone fixing this comment :D

munrocket avatar Oct 04 '21 14:10 munrocket

Just chiming in to this old issue:

The current approach is (1).

For users who know what they're doing, you can achieve (2) by converting a Number to a String e.g. with toFixed, producing a String that with a large number of significant digits, then constructing a decimal from that string. (But NB if you that input string has more than 34 significant digits, the resulting decimal will be obtained by first rounding the input value.)

jessealama avatar Jul 22 '24 14:07 jessealama