Skip to content

Commit

Permalink
Make DoubleRange use ulp instead of custom thing
Browse files Browse the repository at this point in the history
  • Loading branch information
qiemem committed Jan 15, 2018
1 parent 610d729 commit 120ed0b
Showing 1 changed file with 12 additions and 41 deletions.
53 changes: 12 additions & 41 deletions netlogo-core/src/main/api/DoubleRange.scala
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
package org.nlogo.api

import java.lang.{Double => JDouble, StrictMath}

case class DoubleRange(start: Double, stop: Double, step: Double = 1.0, inclusive: Boolean = false)
extends IndexedSeq[Double] {
if (step == 0) throw new IllegalArgumentException("`step` must be nonzero")

// Calculate the scale of precision of the number of the quotient that
// determines the number of elements in the sequence. Error can occur in the
// last bit of this number. Note, we may want to extend this to the last two
// bits due to accumulated error from multiple operations.
val exponentPrecision = doubleScale((stop - start) / step)
// So we get a number that essentially has a one at that bit...
val epsilon = StrictMath.pow(2, -52) * StrictMath.pow(2, exponentPrecision)

override val length = {
val l = if (inclusive)
// We want, for instance, 9.999999999999998 to become 10 instead of 9
// from the floor, so we add the epsilon here.
StrictMath.floor((stop - start) / step + epsilon + 1)
else
// We want, for instance, 10.000000000000002 to become 10 instead of 11
// from the ceil, so we subtract the epsilon here.
StrictMath.ceil((stop - start) / step - epsilon)
override val length: Int = {
val l = if (inclusive) {
// We want, for instance, 9.999999999999998 to become 10 instead of 9
// from the floor, so we add the ulp here.
val num = (stop - start) / step + 1
StrictMath.floor(num + StrictMath.ulp(num))
} else {
// We want, for instance, 10.000000000000002 to become 10 instead of 11
// from the ceil, so we subtract the ulp here.
val num = (stop - start) / step
StrictMath.ceil(num - StrictMath.ulp(num))
}

if (l > Integer.MAX_VALUE)
throw new IllegalArgumentException("Range results in too many elements.")
Expand All @@ -37,26 +30,4 @@ case class DoubleRange(start: Double, stop: Double, step: Double = 1.0, inclusiv

override def tail = DoubleRange(start + step, stop, step, inclusive)
override def init = DoubleRange(start, stop - step, step, inclusive)

/**
* Quantifies the scale of the given double. Usually this is the exponent
* portion of the double bit representation, corrected for its zero offset.
* 0 is given a scale of 1023, however, so that it's always considered less
* specific, so to speak, than any other number.
*/
private def doubleScale(x: Double): Long = {
if (x == 0)
1023
else {
val bits = JDouble.doubleToRawLongBits(x)
// Cut off the significand, which is the last 52 bits
val rightShifted = bits >>> 52
// This leaves us with 12 bits, where the first one is the sign and the
// last 11 are the exponent + 1023
// So, we use an 11 bit mask to drop the sign.
val mask = (1L << 11) - 1L
// And then subtract 1023 to correct for the zero offset (0 is -1023)
(rightShifted & mask) - 1023
}
}
}

0 comments on commit 120ed0b

Please sign in to comment.