Implement in JS nextUp(), nextDown(), nextTowards() and ulp extensions
#KT-4900
This commit is contained in:
@@ -476,6 +476,62 @@ public fun Double.withSign(sign: Double): Double {
|
||||
@InlineOnly
|
||||
public inline fun Double.withSign(sign: Int): Double = this.withSign(sign.toDouble())
|
||||
|
||||
/**
|
||||
* Returns the ulp (unit in the last place) 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.MIN_VALUE`
|
||||
*/
|
||||
@SinceKotlin("1.2")
|
||||
public val Double.ulp: Double get() = when {
|
||||
this < 0 -> (-this).ulp
|
||||
this.isNaN() || this == Double.POSITIVE_INFINITY -> this
|
||||
this == Double.MAX_VALUE -> this - this.nextDown()
|
||||
else -> this.nextUp() - this
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Double] value nearest to this value in direction of positive infinity.
|
||||
*/
|
||||
@SinceKotlin("1.2")
|
||||
public fun Double.nextUp(): Double = when {
|
||||
this.isNaN() || this == Double.POSITIVE_INFINITY -> this
|
||||
this == 0.0 -> Double.MIN_VALUE
|
||||
else -> Double.fromBits(this.toRawBits() + if (this > 0) 1 else -1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Double] value nearest to this value in direction of negative infinity.
|
||||
*/
|
||||
@SinceKotlin("1.2")
|
||||
public fun Double.nextDown(): Double = when {
|
||||
this.isNaN() || this == Double.NEGATIVE_INFINITY -> this
|
||||
this == 0.0 -> -Double.MIN_VALUE
|
||||
else -> Double.fromBits(this.toRawBits() + if (this > 0) -1 else 1)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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`
|
||||
*
|
||||
*/
|
||||
@SinceKotlin("1.2")
|
||||
public fun Double.nextTowards(to: Double): Double = when {
|
||||
this.isNaN() || to.isNaN() -> Double.NaN
|
||||
to == this -> to
|
||||
to > this -> this.nextUp()
|
||||
else /* to < this */-> this.nextDown()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rounds this [Double] value to the nearest integer and converts the result to [Int].
|
||||
* Ties are rounded towards positive infinity.
|
||||
|
||||
@@ -88,7 +88,7 @@ function imul(a, b) {
|
||||
Kotlin.doubleSignBit = function(value) {
|
||||
bufFloat64[0] = value;
|
||||
return bufInt32[1] & 0x80000000;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
|
||||
@@ -428,6 +428,41 @@ public expect fun Double.withSign(sign: Double): Double
|
||||
@SinceKotlin("1.2")
|
||||
public expect fun Double.withSign(sign: Int): Double
|
||||
|
||||
/**
|
||||
* Returns the ulp (unit in the last place) 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.MIN_VALUE`
|
||||
*/
|
||||
@SinceKotlin("1.2")
|
||||
public expect val Double.ulp: Double
|
||||
|
||||
/**
|
||||
* Returns the [Double] value nearest to this value in direction of positive infinity.
|
||||
*/
|
||||
@SinceKotlin("1.2")
|
||||
public expect fun Double.nextUp(): Double
|
||||
/**
|
||||
* Returns the [Double] value nearest to this value in direction of negative infinity.
|
||||
*/
|
||||
@SinceKotlin("1.2")
|
||||
public expect fun Double.nextDown(): Double
|
||||
|
||||
/**
|
||||
* 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`
|
||||
*
|
||||
*/
|
||||
@SinceKotlin("1.2")
|
||||
public expect fun Double.nextTowards(to: Double): Double
|
||||
|
||||
/**
|
||||
* Rounds this [Double] value to the nearest integer and converts the result to [Int].
|
||||
* Ties are rounded towards positive infinity.
|
||||
|
||||
@@ -555,14 +555,14 @@ public inline fun Double.withSign(sign: Double): Double = nativeMath.copySign(th
|
||||
public inline fun Double.withSign(sign: Int): Double = nativeMath.copySign(this, sign.toDouble())
|
||||
|
||||
/**
|
||||
* Returns the ulp of this value.
|
||||
* Returns the ulp (unit in the last place) 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`
|
||||
* - `0.0.ulp` is `Double.MIN_VALUE`
|
||||
*/
|
||||
@SinceKotlin("1.2")
|
||||
@InlineOnly
|
||||
|
||||
@@ -32,38 +32,4 @@ class MathJVMTest {
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,6 +321,50 @@ class DoubleMathTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@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())
|
||||
|
||||
assertEquals(Double.MIN_VALUE, (0.0).ulp)
|
||||
assertEquals(Double.MIN_VALUE, (-0.0).ulp)
|
||||
assertEquals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY.ulp)
|
||||
assertEquals(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.ulp)
|
||||
|
||||
val maxUlp = 2.0.pow(971)
|
||||
assertEquals(maxUlp, Double.MAX_VALUE.ulp)
|
||||
assertEquals(maxUlp, (-Double.MAX_VALUE).ulp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FloatMathTest {
|
||||
|
||||
Reference in New Issue
Block a user