tc39 / proposal-decimal

Built-in exact decimal numbers for JavaScript
http://tc39.es/proposal-decimal/
496 stars 18 forks source link

TypeError on mixed operands #10

Open littledan opened 4 years ago

littledan commented 4 years ago

This proposal follows BigInt in TypeErrors on mixed operands, e.g., BigInt + BigDecimal -> TypeError. Although it is possible to losslessly convert any Number or BigInt to a BigDecimal, the strictness here is intended to be part of a consistent mental model that developers should keep track of their numerical types and ensure uniformity. (The exception is comparison operators, which allow mixed types.) If people have concerns about this model, let's talk them through in this thread.

chicoxyzzy commented 4 years ago

It would be really confusing if mixed Number and BigDecimal expression will not throw. This can lead to hard to debug errors. Mixed BigInt and BigDecimal expressions make more sense, though it probably will be safer to cast to BigDecimal explicitly.

littledan commented 4 years ago

What makes the errors hard to debug?

chicoxyzzy commented 4 years ago

Sorry, "mistake" is a better word. I mean that if developer will mix Number and BigDecimal by mistake, it will be hard to find out if and why the number of decimal places was changed. In financial web apps it's a very common group of bugs (MPI, pips and a lot of other things may have different number of decimal places and that number is very important and shouldn't be changed accidentally)

littledan commented 4 years ago

Note, if you have a bunch of BigDecimal values with different numbers of decimal places, it would be fine to mix them in calculations--they are all the same type.

MaxGraey commented 4 years ago

I guess @chicoxyzzy mean mixed Number (binary 64-bits floats) with BigDecimal (decimal floats with arbitrary precision). This definitely shouldn't be possible implicitly.

littledan commented 4 years ago

Would it solve this problem if you could use the BigDecimal constructor to explicitly cast the Number to a BigDecimal before performing the operation?

chicoxyzzy commented 4 years ago

If it’ll be possible to set a number of decimal places via constructor parameter or at least round by some BigDecimal instance method after the cast, then yes

MaxGraey commented 4 years ago

Exactly. The same way as we do this for mixing BigInt with Number nowadays.

littledan commented 4 years ago

I imagine the constructor will give the exact value (to keep things simple), and then you could use a round method on the result. If you could document requirements for a round method in #14 (or should we break it out into a separate issue?), that would be great

waldemarhorwat commented 4 years ago

I'm not sure I'm happy with the combination of * always returning exact results and BigDecimal having arbitrary precision. It's too easy for the precision to run away from you if you're not careful, and I suspect most users won't be careful. You can do simple things like squaring 1.0000001m 30 times and running out of memory despite the result being fairly small. This is not an issue with IEEE decimal because it does round.

littledan commented 4 years ago

@waldemarhorwat Do you think we should limit to IEEE decimal size, as suggested in #8 ? Maybe that would be a helpful place to follow up.

waldemarhorwat commented 4 years ago

It's a fairly broad question. I think that IEEE decimal (without distinguishing cohorts) has more predictable behavior and applicability — you can do math, trigonometric functions, etc., and they behave just as in IEEE double without worries about falling off a cliff because precision is exploding somewhere inside your algorithm. Unlimited precision decimal is much harder to use safely. Similarly, I'd much rather get an infinity than an exception if I try to compute the tangent of 90°.

jessealama commented 1 year ago

@waldemarhorwat Perhaps one way to allay the concern about trigonometric functions and other elementary functions would be to indicate that those operations need to have a precision attached to them? In other words, only the "easy" stuff like addition, subtraction, multiplication, and division (and, possibly, a couple more operations) work in an unlimited way (though one may optionally specify a precision there, too), but logarithms, sine, cosine, etc., require a precision.