Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BigDecimal Division needs MathContext #1812

Closed
scabug opened this issue Mar 21, 2009 · 13 comments
Closed

BigDecimal Division needs MathContext #1812

scabug opened this issue Mar 21, 2009 · 13 comments
Assignees
Milestone

Comments

@scabug
Copy link

scabug commented Mar 21, 2009

without MathContext there is a risk of ArithmeticException

val one  :BigDecimal = 1
val three:BigDecimal = 3

val result = one/three

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
	at java.math.BigDecimal.divide(BigDecimal.java:1514)
	at scala.BigDecimal.$$div(BigDecimal.scala:168)
	at .<init>(<console>:7)
	at .<clinit>(<console>)
	at RequestResult$$.<init>(<console>:3)
	at RequestResult$$.<clinit>(<console>)
	at RequestResult$$result(<console>)
	at sun.reflect....

java.math.BigDecimal has overloaded versions of divide operation

 BigDecimal  divide(BigDecimal divisor, int scale, RoundingMode roundingMode) 
 BigDecimal  divide(BigDecimal divisor, MathContext mc) 
@scabug
Copy link
Author

scabug commented Mar 21, 2009

Imported From: https://issues.scala-lang.org/browse/SI-1812?orig=1
Reporter: Frank Teubler (dft)
Attachments:

  • Decimal.scala (created on May 12, 2009 12:17:26 AM UTC, 2511 bytes)

@scabug
Copy link
Author

scabug commented May 12, 2009

@eengbrec said:
alternative decimal implementation

@scabug
Copy link
Author

scabug commented May 12, 2009

@eengbrec said:
I've attached an alternative decimal implementation that makes it so (1) a decimal always has a MathContext, and (2) the MathContext is part of the type. Here's a sample interaction:

Welcome to Scala version 2.8.0.r0-b20090511215951 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_07).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val a = Decimal64(1)
a: Decimal64.Decimal = 1

scala> val b = Decimal64(3)
b: Decimal64.Decimal = 3

scala> a / b
res0: Decimal64.Decimal = 0.3333333333333333

scala> val c = Decimal32(3)
c: Decimal32.Decimal = 3

scala> a / c
<console>:7: error: type mismatch;
 found   : Decimal32.Decimal
 required: Decimal64.Decimal
       a / c
           ^

scala> 

As you can see it is well-behaved and won't let you randomly mix Decimals from different DecimalModule.

@scabug
Copy link
Author

scabug commented May 14, 2009

@odersky said:
Toni, can you take care of this? Thanks -- Martin

@scabug
Copy link
Author

scabug commented May 15, 2009

Frank Teubler (dft) said:
Replying to [comment:4 eengbrec]:

I see that BigDecimals with different contexts
can not be mixed. But in real life applications
I have sometimes to move to higher precision
at runtime, or there is a mix of different algorithms
with different precisions. How to convert?

An alternative suggestion:

If there were a global (thread local?) MathContext variable,
I could switch the context back and forth as required.

@scabug
Copy link
Author

scabug commented May 15, 2009

@eengbrec said:
I could add a conversion method.

@scabug
Copy link
Author

scabug commented May 15, 2009

@eengbrec said:
An alternative implementation could add the MathContext as an implicit parameter. Then the user defines an implicit val/def that supplies it. The problem is that implicit parameter needs to propagate everywhere, so it adds a lot of clutter. It also doesn't leverage the type system to ensure any sort of safety, which I think would be a significant drawback.

@scabug
Copy link
Author

scabug commented Jun 10, 2009

@paulp said:
OK, let me know what you think of r18021. I did not bring MathContext into the type system and am not very convinced it's wise. I did make MathContext a part of BigDecimal, and it uses the mc on every operation.

You can change contexts with an apply method on BigDecimal, so:

scala> val x = BigDecimal(1.0)
x: BigDecimal = 1.0

scala> x / 3
java.lang.ArithmeticException: Non-terminating decimal expansion [...]

scala> x(DECIMAL32) / 3
res5: BigDecimal = 0.3333333

scala> x(DECIMAL64) / 3
res6: BigDecimal = 0.3333333333333333

Also I added some new methods, like:

scala> BigDecimal(100) /% 17
res7: (BigDecimal, BigDecimal) = (5,15)

scala> BigDecimal(100) quot 17
res8: BigDecimal = 5

scala> BigDecimal(100.0001).byteValueExact
java.lang.ArithmeticException: Rounding necessary

And etc.

@scabug
Copy link
Author

scabug commented Jun 11, 2009

@eengbrec said:
Why do you think brining MathContext into the type system may be unwise? In most cases the correct thing to do is to use a single MC and not mix them. Encoding it into the type system ensures that they are not mixed.

What if the MC were encoded as a covariant type parameter instead? Then if someone really didn't care, they could use a base type on the parameter that would allow it to be mixed freely. It would take a little thought to work out, but probably not too much. Let me know what you're thinking against encoding the MC in the type system is, and I'll see if I can come up with something.

Also, if the MC is not part of the type, then I would suggest using a more friendly MC as the default. Crashing on non-terminating expansions is really not a friendly default behavior.

Oh...and why are we stuck with Sun's awful name? Yeah, I know in the 2.7 series it was already called BigDemical, but really. There's no "SmallDecimal" or "RegularDecimal" in the library. Just BigDecimal. In the name of good names I suggest adding a deprecated type alias mapping BigDecimal to a more appropriately name Decimal.

@scabug
Copy link
Author

scabug commented Mar 23, 2011

@paulp said:
(In r24547) My early attempts to implement non-integral ranges in a way which
was useful without having lots of floating point traps were less
than successful. One of the bigger backfires is that the requirement
not to round (trying, and failing anyway, to avoid surprises with
methods like "contains") inflicts runtime errors.

The simple way to improve this, which seems a good idea anyway,
is to make the default math context something less inclined to
exceptions. Default BigDecimal mc is now DECIMAL128.
References #1812, #4201 and puts #4201 back to normal priority.
Review by community.

@scabug
Copy link
Author

scabug commented Mar 31, 2011

@SethTisue said:
one of my project euler solutions (which are of life-or-death importance, I assure you) was failing until I figured out that the change to the default math context for BigDecimal was the culprit. may I suggest a migration warning? (I note that BigDecimal.scala imports annotation.migration, but then does not use it)

@scabug scabug closed this as completed May 18, 2011
@scabug
Copy link
Author

scabug commented Nov 28, 2011

robinst said:
This should at least be mentioned in the scaladoc, as it's a surprising difference to the default Java behavior. (I had to look at the source to find out about this, and then followed the issue links in "git blame" to come here.)

@scabug
Copy link
Author

scabug commented Dec 14, 2011

@dchenbecker said:
Agree with robinst. I'm not sure that trying to protect the user from runtime errors trumps behavior consistent with the Java library. We don't for example, automatically promote Ints to Longs when we have an overflow on addition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants