Use HALF_EVEN rounding mode for round()
Add docs and tests for rounding. #KT-4900
This commit is contained in:
@@ -266,10 +266,52 @@ public inline fun log2(a: Double): Double = nativeMath.log2(a)
|
||||
@InlineOnly
|
||||
public inline fun log1p(a: Double): Double = nativeMath.log1p(a)
|
||||
|
||||
/**
|
||||
* Rounds the given value [a] to an integer towards positive infinity.
|
||||
|
||||
inline fun ceil(a: Double): Double = nativeMath.ceil(a).unsafeCast<Double>() // TODO: Remove unsafe cast after removing public js.math
|
||||
inline fun floor(a: Double): Double = nativeMath.floor(a).unsafeCast<Double>()
|
||||
inline fun truncate(a: Double): Double = nativeMath.trunc(a) // polyfill
|
||||
* @return the smallest double value that is greater than the given value [a] and is a mathematical integer.
|
||||
*
|
||||
* Special cases:
|
||||
* - `ceil(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer.
|
||||
*/
|
||||
@InlineOnly
|
||||
public inline fun ceil(a: Double): Double = nativeMath.ceil(a).unsafeCast<Double>() // TODO: Remove unsafe cast after removing public js.math
|
||||
|
||||
/**
|
||||
* Rounds the given value [a] to an integer towards negative infinity.
|
||||
|
||||
* @return the largest double value that is smaller than the given value [a] and is a mathematical integer.
|
||||
*
|
||||
* Special cases:
|
||||
* - `floor(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer.
|
||||
*/
|
||||
@InlineOnly
|
||||
public inline fun floor(a: Double): Double = nativeMath.floor(a).unsafeCast<Double>()
|
||||
|
||||
/**
|
||||
* Rounds the given value [a] to an integer towards zero.
|
||||
*
|
||||
* @return the value [a] having its fractional part truncated.
|
||||
*
|
||||
* Special cases:
|
||||
* - `truncate(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer.
|
||||
*/
|
||||
@InlineOnly
|
||||
public inline fun truncate(a: Double): Double = nativeMath.trunc(a)
|
||||
|
||||
/**
|
||||
* Rounds the given value [a] towards the closest integer with ties rounded towards even integer.
|
||||
*
|
||||
* Special cases:
|
||||
* - `round(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer.
|
||||
*/
|
||||
public fun round(a: Double): Double {
|
||||
if (a % 0.5 != 0.0) {
|
||||
return nativeMath.round(a).unsafeCast<Double>()
|
||||
}
|
||||
val floor = floor(a)
|
||||
return if (floor % 2 == 0.0) floor else ceil(a)
|
||||
}
|
||||
|
||||
// also as extension val [absoluteValue]
|
||||
inline fun abs(a: Double): Double = nativeMath.abs(a)
|
||||
@@ -302,8 +344,39 @@ inline val Double.sign: Double get() = nativeMath.sign(this)
|
||||
fun Double.withSign(sign: Double): Double = this.absoluteValue * sign.sign
|
||||
inline fun Double.withSign(sign: Int): Double = this.withSign(sign.toDouble())
|
||||
|
||||
/**
|
||||
* Rounds this [Double] value to the nearest integer and converts the result to [Int].
|
||||
* Ties are rounded towards positive infinity.
|
||||
*
|
||||
* Special cases:
|
||||
* - `x.roundToInt() == Int.MAX_VALUE` when `x > Int.MAX_VALUE`
|
||||
* - `x.roundToInt() == Int.MIN_VALUE` when `x < Int.MIN_VALUE`
|
||||
*
|
||||
* @throws IllegalArgumentException when this value is `NaN`
|
||||
*/
|
||||
public fun Double.roundToInt(): Int = when {
|
||||
isNaN() -> throw IllegalArgumentException("Cannot round NaN value.")
|
||||
this > Int.MAX_VALUE -> Int.MAX_VALUE
|
||||
this < Int.MIN_VALUE -> Int.MIN_VALUE
|
||||
else -> nativeMath.round(this).unsafeCast<Double>().toInt()
|
||||
}
|
||||
|
||||
fun Double.roundToLong(): Long = if (isNaN()) throw IllegalArgumentException("Cannot round NaN value.") else nativeMath.round(this).unsafeCast<Double>().toLong()
|
||||
/**
|
||||
* Rounds this [Double] value to the nearest integer and converts the result to [Long].
|
||||
* Ties are rounded towards positive infinity.
|
||||
*
|
||||
* Special cases:
|
||||
* - `x.roundToLong() == Long.MAX_VALUE` when `x > Long.MAX_VALUE`
|
||||
* - `x.roundToLong() == Long.MIN_VALUE` when `x < Long.MIN_VALUE`
|
||||
*
|
||||
* @throws IllegalArgumentException when this value is `NaN`
|
||||
*/
|
||||
public fun Double.roundToLong(): Long = when {
|
||||
isNaN() -> throw IllegalArgumentException("Cannot round NaN value.")
|
||||
this > Long.MAX_VALUE -> Long.MAX_VALUE
|
||||
this < Long.MIN_VALUE -> Long.MIN_VALUE
|
||||
else -> nativeMath.round(this).unsafeCast<Double>().toLong()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -325,7 +398,7 @@ inline fun Float.withSign(sign: Float): Float = this.toDouble().withSign(sign.to
|
||||
inline fun Float.withSign(sign: Int): Float = this.toDouble().withSign(sign.toDouble()).toFloat()
|
||||
|
||||
|
||||
fun Float.roundToInt(): Int = if (isNaN()) throw IllegalArgumentException("Cannot round NaN value.") else nativeMath.round(this)
|
||||
fun Float.roundToInt(): Int = toDouble().roundToInt()
|
||||
fun Float.roundToLong(): Long = toDouble().roundToLong()
|
||||
|
||||
|
||||
|
||||
@@ -268,13 +268,61 @@ public fun log2(a: Double): Double = nativeMath.log(a) / LN2
|
||||
@InlineOnly
|
||||
public inline fun log1p(a: Double): Double = nativeMath.log1p(a)
|
||||
|
||||
public inline fun ceil(a: Double): Double = nativeMath.ceil(a)
|
||||
public inline fun floor(a: Double): Double = nativeMath.floor(a)
|
||||
public inline fun truncate(a: Double): Double = nativeMath.rint(a)
|
||||
/**
|
||||
* Rounds the given value [a] to an integer towards positive infinity.
|
||||
|
||||
// also as extension val [absoluteValue]
|
||||
* @return the smallest double value that is greater than the given value [a] and is a mathematical integer.
|
||||
*
|
||||
* Special cases:
|
||||
* - `ceil(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer.
|
||||
*/
|
||||
@InlineOnly
|
||||
public inline fun ceil(a: Double): Double = nativeMath.ceil(a)
|
||||
|
||||
/**
|
||||
* Rounds the given value [a] to an integer towards negative infinity.
|
||||
|
||||
* @return the largest double value that is smaller than the given value [a] and is a mathematical integer.
|
||||
*
|
||||
* Special cases:
|
||||
* - `floor(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer.
|
||||
*/
|
||||
@InlineOnly
|
||||
public inline fun floor(a: Double): Double = nativeMath.floor(a)
|
||||
|
||||
/**
|
||||
* Rounds the given value [a] to an integer towards zero.
|
||||
*
|
||||
* @return the value [a] having its fractional part truncated.
|
||||
*
|
||||
* Special cases:
|
||||
* - `truncate(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer.
|
||||
*/
|
||||
public fun truncate(a: Double): Double = when {
|
||||
a.isNaN() || a.isInfinite() -> a
|
||||
a > 0 -> floor(a)
|
||||
else -> ceil(a)
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given value [a] towards the closest integer with ties rounded towards even integer.
|
||||
*
|
||||
* Special cases:
|
||||
* - `round(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer.
|
||||
*/
|
||||
@InlineOnly
|
||||
public inline fun round(a: Double): Double = nativeMath.rint(a)
|
||||
|
||||
|
||||
/**
|
||||
* Returns the absolute value of the given value [a].
|
||||
*
|
||||
* @see absoluteValue extension property for [Double]
|
||||
*/
|
||||
@InlineOnly
|
||||
public inline fun abs(a: Double): Double = nativeMath.abs(a)
|
||||
// also as extension val [sign]
|
||||
@InlineOnly
|
||||
public inline fun sgn(a: Double): Double = nativeMath.signum(a)
|
||||
|
||||
|
||||
@@ -327,6 +375,33 @@ public inline fun Double.nextUp(): Double = nativeMath.nextUp(this)
|
||||
public inline fun Double.nextDown(): Double = nativeMath.nextAfter(this, Double.NEGATIVE_INFINITY)
|
||||
public inline fun Double.nextTowards(to: Double): Double = nativeMath.nextAfter(this, to)
|
||||
|
||||
/**
|
||||
* Rounds this [Double] value to the nearest integer and converts the result to [Int].
|
||||
* Ties are rounded towards positive infinity.
|
||||
*
|
||||
* Special cases:
|
||||
* - `x.roundToInt() == Int.MAX_VALUE` when `x > Int.MAX_VALUE`
|
||||
* - `x.roundToInt() == Int.MIN_VALUE` when `x < Int.MIN_VALUE`
|
||||
*
|
||||
* @throws IllegalArgumentException when this value is `NaN`
|
||||
*/
|
||||
public fun Double.roundToInt(): Int = when {
|
||||
isNaN() -> throw IllegalArgumentException("Cannot round NaN value.")
|
||||
this > Int.MAX_VALUE -> Int.MAX_VALUE
|
||||
this < Int.MIN_VALUE -> Int.MIN_VALUE
|
||||
else -> nativeMath.round(this).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds this [Double] value to the nearest integer and converts the result to [Long].
|
||||
* Ties are rounded towards positive infinity.
|
||||
*
|
||||
* Special cases:
|
||||
* - `x.roundToLong() == Long.MAX_VALUE` when `x > Long.MAX_VALUE`
|
||||
* - `x.roundToLong() == Long.MIN_VALUE` when `x < Long.MIN_VALUE`
|
||||
*
|
||||
* @throws IllegalArgumentException when this value is `NaN`
|
||||
*/
|
||||
public fun Double.roundToLong(): Long = if (isNaN()) throw IllegalArgumentException("Cannot round NaN value.") else nativeMath.round(this)
|
||||
|
||||
|
||||
|
||||
@@ -148,9 +148,64 @@ class DoubleMathTest {
|
||||
}
|
||||
|
||||
@Test fun rounding() {
|
||||
TODO()
|
||||
for (value in listOf(Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 0.0, 1.0, -10.0)) {
|
||||
assertEquals(value, ceil(value))
|
||||
assertEquals(value, floor(value))
|
||||
assertEquals(value, truncate(value))
|
||||
assertEquals(value, round(value))
|
||||
}
|
||||
val data = arrayOf( // v floor trunc round ceil
|
||||
doubleArrayOf( 1.3, 1.0, 1.0, 1.0, 2.0),
|
||||
doubleArrayOf(-1.3, -2.0, -1.0, -1.0, -1.0),
|
||||
doubleArrayOf( 1.5, 1.0, 1.0, 2.0, 2.0),
|
||||
doubleArrayOf(-1.5, -2.0, -1.0, -2.0, -1.0),
|
||||
doubleArrayOf( 1.8, 1.0, 1.0, 2.0, 2.0),
|
||||
doubleArrayOf(-1.8, -2.0, -1.0, -2.0, -1.0),
|
||||
|
||||
doubleArrayOf( 2.3, 2.0, 2.0, 2.0, 3.0),
|
||||
doubleArrayOf(-2.3, -3.0, -2.0, -2.0, -2.0),
|
||||
doubleArrayOf( 2.5, 2.0, 2.0, 2.0, 3.0),
|
||||
doubleArrayOf(-2.5, -3.0, -2.0, -2.0, -2.0),
|
||||
doubleArrayOf( 2.8, 2.0, 2.0, 3.0, 3.0),
|
||||
doubleArrayOf(-2.8, -3.0, -2.0, -3.0, -2.0)
|
||||
)
|
||||
for ((v, f, t, r, c) in data) {
|
||||
assertEquals(f, floor(v), "floor($v)")
|
||||
assertEquals(t, truncate(v), "truncate($v)")
|
||||
assertEquals(r, round(v), "round($v)")
|
||||
assertEquals(c, ceil(v), "ceil($v)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun roundingConversion() {
|
||||
assertEquals(1L, 1.0.roundToLong())
|
||||
assertEquals(1L, 1.1.roundToLong())
|
||||
assertEquals(2L, 1.5.roundToLong())
|
||||
assertEquals(3L, 2.5.roundToLong())
|
||||
assertEquals(-2L, (-2.5).roundToLong())
|
||||
assertEquals(-3L, (-2.6).roundToLong())
|
||||
assertEquals(9223372036854774784, (9223372036854774800.0).roundToLong())
|
||||
assertEquals(Long.MAX_VALUE, Double.MAX_VALUE.roundToLong())
|
||||
assertEquals(Long.MIN_VALUE, (-Double.MAX_VALUE).roundToLong())
|
||||
assertEquals(Long.MAX_VALUE, Double.POSITIVE_INFINITY.roundToLong())
|
||||
assertEquals(Long.MIN_VALUE, Double.NEGATIVE_INFINITY.roundToLong())
|
||||
|
||||
assertFails { Double.NaN.roundToLong() }
|
||||
|
||||
assertEquals(1, 1.0.roundToInt())
|
||||
assertEquals(1, 1.1.roundToInt())
|
||||
assertEquals(2, 1.5.roundToInt())
|
||||
assertEquals(3, 2.5.roundToInt())
|
||||
assertEquals(-2, (-2.5).roundToInt())
|
||||
assertEquals(-3, (-2.6).roundToInt())
|
||||
assertEquals(2123456789, (2123456789.0).roundToInt())
|
||||
assertEquals(Int.MAX_VALUE, Double.MAX_VALUE.roundToInt())
|
||||
assertEquals(Int.MIN_VALUE, (-Double.MAX_VALUE).roundToInt())
|
||||
assertEquals(Int.MAX_VALUE, Double.POSITIVE_INFINITY.roundToInt())
|
||||
assertEquals(Int.MIN_VALUE, Double.NEGATIVE_INFINITY.roundToInt())
|
||||
|
||||
assertFails { Double.NaN.roundToInt() }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user