V P - 3 months ago 11

Scala Question

I want to check if adding some value to a double value exceed the Double limits or not. I tried this:

`object Hello {`

def main(args: Array[String]): Unit = {

var t = Double.MaxValue

var t2 = t+100000000

if(t2 > 0) {

println("t2 > 0: " + t2)

} else

println("t2 <= 0: " + t2)

}

}

The output I get is

`t2 > 0: 1.7976931348623157E308`

What I actually want is to sum billions of values and check whether or not the running sum overflows at any time.

Answer

The first part of your question seems to stem from a misunderstanding of floating-point numbers.

IEEE-754 floating-point numbers do not wrap around like some finite-size integers would. Instead, they "saturate" at

`Double.PositiveInfinity`

, which represents mathematical infinity.`Double.MaxValue`

is the largest finite positive value of doubles. The next`Double`

after that is`Double.PositiveInfinity`

. Adding any double other than`Double.NegativeInfinity`

and`NaN`

s to`Double.PositiveInfinity`

yields`Double.PositiveInfinity`

.`scala> Double.PositiveInfinity + 1 res0: Double = Infinity scala> Double.PositiveInfinity - 1 res1: Double = Infinity scala> Double.PositiveInfinity + Double.NaN res2: Double = NaN scala> Double.PositiveInfinity + Double.NegativeInfinity res3: Double = NaN`

Floating-point numbers get fewer and farther between as their magnitude grows.

`Double.MaxValue + 100000000`

evaluates to`Double.MaxValue`

as a result of roundoff error:`Double.MaxValue`

is so much larger than`100000000`

that the former "swallows up" the latter if you try to add them. You would need to add a`Double`

of the order of`math.pow(2, -52) * Double.MaxValue`

to`Double.MaxValue`

in order to get`Double.PositiveInfinity`

:`scala> math.pow(2,-52) * Double.MaxValue + Double.MaxValue res4: Double = Infinity`

Now, you write

What I actually want is to sum billions of values and check whether or not the running sum overflows at any time.

One possible approach is to define a function that adds the numbers recursively but stops if the running sum is an infinity or a `NaN`

, and wraps the result in an `Either[String, Double]`

:

```
import scala.collection.immutable
def sumToEither(xs: immutable.Seq[Double]): Either[String, Double] = {
@annotation.tailrec
def go(ys: immutable.Seq[Double], acc: Double): Double =
if (ys.isEmpty || acc.isInfinite || acc.isNaN) acc
else go(ys.tail, ys.head + acc)
go(xs, 0.0) match {
case x if x.isInfinite => Left("overflow")
case x if x.isNaN => Left("NaN")
case x => Right(x)
}
}
```