From 908009bf42973a50bd0eea8f5de7fa60ced09370 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Thu, 14 Feb 2019 17:22:26 +0300 Subject: [PATCH] Improve double-to-ulong conversion tests #KT-27108 --- libraries/stdlib/test/unsigned/ULongTest.kt | 29 +++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/libraries/stdlib/test/unsigned/ULongTest.kt b/libraries/stdlib/test/unsigned/ULongTest.kt index 8a8429d0b98..e325f7e5dbc 100644 --- a/libraries/stdlib/test/unsigned/ULongTest.kt +++ b/libraries/stdlib/test/unsigned/ULongTest.kt @@ -119,6 +119,7 @@ class ULongTest { val float = Long.MAX_VALUE.toFloat() + long.toFloat() // We lose accuracy here, hence `eps` is used. val ulong = Long.MAX_VALUE.toULong() + long.toULong() + // TODO: replace with ulp comparison when available on Float val eps = 1e+13 assertTrue(abs(float - ulong.toFloat()) < eps) } @@ -141,11 +142,12 @@ class ULongTest { repeat(100) { val long = Random.nextLong(from = 0, until = Long.MAX_VALUE) - val double = Long.MAX_VALUE.toDouble() + long.toDouble() // We lose accuracy here, hence `eps` is used. - val ulong = Long.MAX_VALUE.toULong() + long.toULong() + val value = Long.MAX_VALUE.toULong() + long.toULong() + val expected = Long.MAX_VALUE.toDouble() + long.toDouble() // Should be accurate to one ulp + val actual = value.toDouble() + val diff = abs(expected - value.toDouble()) - val eps = 1e+4 - assertTrue(abs(double - ulong.toDouble()) < eps) + assertTrue(diff <= actual.ulp, "$actual should be within one ulp (${actual.ulp}) from the expected $expected") } fun testRounding(from: ULong, count: UInt) { @@ -208,14 +210,11 @@ class ULongTest { } repeat(100) { - val diff = Random.nextDouble() * Long.MAX_VALUE - val d = Long.MAX_VALUE.toDouble() + diff - val ul = Long.MAX_VALUE.toULong() + diff.toLong().toULong() - val eps = 1e-6 + val d = 2.0.pow(63) * (1 + Random.nextDouble()) + val expected = specialDoubleToULong(d) + val actual = d.toULong() - assertTrue(d.toULong() / ul <= 1u) - assertTrue(d / ul.toDouble() < 1 + eps) - assertTrue(d / ul.toDouble() > 1 - eps) + assertEquals(expected, actual, "Expected bit pattern: ${expected.toString(2)}, actual bit pattern: ${actual.toString(2)}") } fun testTrailingBits(v: Double, count: Int) { @@ -241,4 +240,12 @@ class ULongTest { testTrailingBits(v, msb - 52) } } + + /** Creates an ULong value directly from mantissa bits of Double that is in range [2^63, 2^64). */ + private fun specialDoubleToULong(v: Double): ULong { + require(v >= 2.0.pow(63)) + require(v < 2.0.pow(64)) + val bits = v.toBits().toULong() + return (1uL shl 63) + ((bits and (1uL shl 52) - 1u) shl 11) + } } \ No newline at end of file