From f3ea499d2ccf0579245f233289b175f17d696be5 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Tue, 25 Jul 2017 18:20:14 +0300 Subject: [PATCH] JVM-specific math functions and tests #KT-4900 --- libraries/stdlib/src/kotlin/util/MathJVM.kt | 44 ++++++++++++- libraries/stdlib/test/numbers/MathJVMTest.kt | 69 ++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 libraries/stdlib/test/numbers/MathJVMTest.kt diff --git a/libraries/stdlib/src/kotlin/util/MathJVM.kt b/libraries/stdlib/src/kotlin/util/MathJVM.kt index 10ffe5653de..f6e7b0df43c 100644 --- a/libraries/stdlib/src/kotlin/util/MathJVM.kt +++ b/libraries/stdlib/src/kotlin/util/MathJVM.kt @@ -358,7 +358,19 @@ public inline fun Double.pow(other: Double): Double = nativeMath.pow(this, other @InlineOnly public inline fun Double.pow(other: Int): Double = nativeMath.pow(this, other.toDouble()) - +/** + * Computes the remainder of division of this value by the [other] value according to the IEEE 754 standard. + * + * The result is computed as `r = this - (q * other)` where `q` is the quotient of division rounded to the nearest integer, + * `q = round(this / other)`. + * + * Special cases: + * - `x.IEEErem(y)` is `NaN`, when `x` is `NaN` or `y` is `NaN` or `x` is `+Inf|-Inf` or `y` is zero. + * - `x.IEEErem(y) == x` when `x` is finite and `y` is infinite. + * + * @see round + */ +@InlineOnly public inline fun Double.IEEErem(other: Double): Double = nativeMath.IEEEremainder(this, other) /** @@ -397,9 +409,39 @@ public inline fun Double.withSign(sign: Double): Double = nativeMath.copySign(th @InlineOnly public inline fun Double.withSign(sign: Int): Double = nativeMath.copySign(this, sign.toDouble()) +/** + * Returns the ulp of this value. + * + * An ulp is a positive distance between this value and the next nearest [Double] value larger in magnitude. + * + * Special Cases: + * - `NaN.ulp` is `NaN` + * - `x.ulp` is `+Inf` when `x` is `+Inf` or `-Inf` + * - `0.0.ulp` is `Double.NIN_VALUE` + */ +@InlineOnly public inline val Double.ulp: Double get() = nativeMath.ulp(this) + +/** + * Returns the [Double] value nearest to this value in direction of positive infinity. + */ +@InlineOnly public inline fun Double.nextUp(): Double = nativeMath.nextUp(this) +/** + * Returns the [Double] value nearest to this value in direction of negative infinity. + */ +@InlineOnly public inline fun Double.nextDown(): Double = nativeMath.nextAfter(this, Double.NEGATIVE_INFINITY) + +/** + * Returns the [Double] value nearest to this value in direction from this value towards the value [to]. + * + * Special cases: + * - `x.nextTowards(y)` is `NaN` if either `x` or `y` are `NaN` + * - `x.nextTowards(x) == x` + * + */ +@InlineOnly public inline fun Double.nextTowards(to: Double): Double = nativeMath.nextAfter(this, to) /** diff --git a/libraries/stdlib/test/numbers/MathJVMTest.kt b/libraries/stdlib/test/numbers/MathJVMTest.kt new file mode 100644 index 00000000000..649ff6655ad --- /dev/null +++ b/libraries/stdlib/test/numbers/MathJVMTest.kt @@ -0,0 +1,69 @@ +@file:kotlin.jvm.JvmVersion +package test.numbers + +import kotlin.test.* +import org.junit.Test +import kotlin.math.* + +class MathJVMTest { + + @Test fun IEEEremainder() { + val data = arrayOf( // a a IEEErem 2.5 + doubleArrayOf(-2.0, 0.5), + doubleArrayOf(-1.25, -1.25), + doubleArrayOf( 0.0, 0.0), + doubleArrayOf( 1.0, 1.0), + doubleArrayOf( 1.25, 1.25), + doubleArrayOf( 1.5, -1.0), + doubleArrayOf( 2.0, -0.5), + doubleArrayOf( 2.5, 0.0), + doubleArrayOf( 3.5, 1.0), + doubleArrayOf( 3.75, -1.25), + doubleArrayOf( 4.0, -1.0) + ) + for ((a, r) in data) { + assertEquals(r, a.IEEErem(2.5), "($a).IEEErem(2.5)") + } + + assertTrue(Double.NaN.IEEErem(2.5).isNaN()) + assertTrue(2.0.IEEErem(Double.NaN).isNaN()) + assertTrue(Double.POSITIVE_INFINITY.IEEErem(2.0).isNaN()) + assertTrue(2.0.IEEErem(0.0).isNaN()) + assertEquals(PI, PI.IEEErem(Double.NEGATIVE_INFINITY)) + } + + @Test fun nextAndPrev() { + for (value in listOf(0.0, -0.0, Double.MIN_VALUE, -1.0, 2.0.pow(10))) { + val next = value.nextUp() + if (next > 0) { + assertEquals(next, value + value.ulp) + } else { + assertEquals(value, next - next.ulp) + } + + val prev = value.nextDown() + if (prev > 0) { + assertEquals(value, prev + prev.ulp) + } + else { + assertEquals(prev, value - value.ulp) + } + + val toZero = value.nextTowards(0.0) + if (toZero != 0.0) { + assertEquals(value, toZero + toZero.ulp.withSign(toZero)) + } + + assertEquals(Double.POSITIVE_INFINITY, Double.MAX_VALUE.nextUp()) + assertEquals(Double.MAX_VALUE, Double.POSITIVE_INFINITY.nextDown()) + + assertEquals(Double.NEGATIVE_INFINITY, (-Double.MAX_VALUE).nextDown()) + assertEquals((-Double.MAX_VALUE), Double.NEGATIVE_INFINITY.nextUp()) + + assertTrue(Double.NaN.ulp.isNaN()) + assertTrue(Double.NaN.nextDown().isNaN()) + assertTrue(Double.NaN.nextUp().isNaN()) + assertTrue(Double.NaN.nextTowards(0.0).isNaN()) + } + } +} \ No newline at end of file