scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
230 stars 21 forks source link

BigInt#isValidDouble produces incorrect results for some large integers #12836

Closed michaelmior closed 11 months ago

michaelmior commented 11 months ago

Reproduction steps

Scala version: 2.13.11

scala> val x = 738270448276026960L
val x: Long = 738270448276026960

scala> BigInt(x).isValidDouble
val res0: Boolean = false

scala> BigInt(x).toDouble == x.toDouble
val res1: Boolean = true

scala> x == x.toDouble
val res2: Boolean = true

Problem

I would expect that since calling toDouble converts to the exact same Double value, that isValidDouble would return true.

Ichoran commented 11 months ago

That number cannot be represented exactly in a Double, so the behavior is correct.

Example:

scala> 738270448276026960L
res0: Long = 738270448276026960

scala> res0.toDouble
res1: Double = 7.3827044827602701E17

scala> res1.toLong
res2: Long = 738270448276027008

A BigInt is a Double if the represented values are identical, not if a lossy conversion one way or the other results in the same thing.

For instance. 2.3 converts to 2, but 2.3 is not a BigInt value.

Ichoran commented 11 months ago

That == uses Double equality for Long values that cannot be precisely represented as a Double is a questionable choice, because it breaks the contract for hashCode (that if a==b then a.## == b.##). But it's not an issue with BigInt; BigInt correctly says it's not the same as the Double representation.

scala> BigInt(res0)
res3: scala.math.BigInt = 738270448276026960

scala> res3 == res1
res4: Boolean = false

scala> res0 == res1  // Um...really?
res5: Boolean = true
michaelmior commented 11 months ago

Apologies as I didn't notice that it clearly isn't the same number. I suppose this can be closed.

som-snytt commented 11 months ago

As a refresher, the spec phrase for the promotion to double is "operation type". (https://scala-lang.org/files/archive/spec/2.13/12-the-scala-standard-library.html#numeric-value-types)

I don't know why it's not called "uncooperative equality".

The change a few years to warn about lossy conversions includes a smarter comment to the effect that once floating point is involved, your spidey sense must kick in to save you from incorrect assumptions about how an integral type will behave.

This is a good opportunity to show appreciation for the brave people who work with numbers. We thank you for your service.