Parsing of decimal values takes 9 immediately digits but not 1.

This commit is contained in:
zhelenskiy 2021-05-12 04:42:17 +03:00
parent 9184bd55de
commit b4e47494fc
3 changed files with 54 additions and 26 deletions

View File

@ -10,10 +10,7 @@ import kotlinx.benchmark.Blackhole
import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.State import org.openjdk.jmh.annotations.State
import space.kscience.kmath.operations.BigInt import space.kscience.kmath.operations.*
import space.kscience.kmath.operations.BigIntField
import space.kscience.kmath.operations.JBigIntegerField
import space.kscience.kmath.operations.invoke
@State(Scope.Benchmark) @State(Scope.Benchmark)
@ -64,4 +61,24 @@ internal class BigIntBenchmark {
fun jvmPower(blackhole: Blackhole) = JBigIntegerField { fun jvmPower(blackhole: Blackhole) = JBigIntegerField {
blackhole.consume(jvmNumber.pow(bigExponent)) blackhole.consume(jvmNumber.pow(bigExponent))
} }
@Benchmark
fun kmParsing16(blackhole: Blackhole) = JBigIntegerField {
blackhole.consume("0x7f57ed8b89c29a3b9a85c7a5b84ca3929c7b7488593".parseBigInteger())
}
@Benchmark
fun kmParsing10(blackhole: Blackhole) = JBigIntegerField {
blackhole.consume("236656783929183747565738292847574838922010".parseBigInteger())
}
@Benchmark
fun jvmParsing10(blackhole: Blackhole) = JBigIntegerField {
blackhole.consume("236656783929183747565738292847574838922010".toBigInteger(10))
}
@Benchmark
fun jvmParsing16(blackhole: Blackhole) = JBigIntegerField {
blackhole.consume("7f57ed8b89c29a3b9a85c7a5b84ca3929c7b7488593".toBigInteger(16))
}
} }

View File

@ -441,13 +441,6 @@ public fun UIntArray.toBigInt(sign: Byte): BigInt {
return BigInt(sign, copyOf()) return BigInt(sign, copyOf())
} }
private val hexChToInt: MutableMap<Char, UInt> = hashMapOf(
'0' to 0U, '1' to 1U, '2' to 2U, '3' to 3U,
'4' to 4U, '5' to 5U, '6' to 6U, '7' to 7U,
'8' to 8U, '9' to 9U, 'A' to 10U, 'B' to 11U,
'C' to 12U, 'D' to 13U, 'E' to 14U, 'F' to 15U
)
/** /**
* Returns null if a valid number can not be read from a string * Returns null if a valid number can not be read from a string
*/ */
@ -475,7 +468,7 @@ public fun String.parseBigInteger(): BigInt? {
return if (this.startsWith("0X", startIndex = positivePartIndex, ignoreCase = true)) { return if (this.startsWith("0X", startIndex = positivePartIndex, ignoreCase = true)) {
// hex representation // hex representation
val uInts = mutableListOf(0U) val uInts = ArrayList<UInt>(length).apply { add(0U) }
var offset = 0 var offset = 0
fun addDigit(value: UInt) { fun addDigit(value: UInt) {
uInts[uInts.lastIndex] += value shl offset uInts[uInts.lastIndex] += value shl offset
@ -502,22 +495,34 @@ public fun String.parseBigInteger(): BigInt? {
if (isEmpty) null else BigInt(sign.toByte(), uInts.toUIntArray()) if (isEmpty) null else BigInt(sign.toByte(), uInts.toUIntArray())
} else { } else {
// hex representation // decimal representation
var res = BigInt.ZERO
var digitValue = BigInt.ONE
for (index in lastIndex downTo positivePartIndex) {
val ch = this[index]
// decimal representation
if (ch == '_') continue
if (ch !in '0'..'9') {
return null
}
res += digitValue * (ch.code - '0'.code)
isEmpty = false
digitValue *= 10.toBigInt()
val positivePart = buildList(length) {
for (index in positivePartIndex until length)
when (val a = this@parseBigInteger[index]) {
'_' -> continue
in '0'..'9' -> add(a)
else -> return null
}
} }
val offset = positivePart.size % 9
isEmpty = offset == 0
fun parseUInt(fromIndex: Int, toIndex: Int): UInt? {
var res = 0U
for (i in fromIndex until toIndex) {
res = res * 10U + (positivePart[i].digitToIntOrNull()?.toUInt() ?: return null)
}
return res
}
var res = parseUInt(0, offset)?.toBigInt() ?: return null
for (index in offset..positivePart.lastIndex step 9) {
isEmpty = false
res = res * 1_000_000_000U + (parseUInt(index, index + 9) ?: return null).toBigInt()
}
if (isEmpty) null else res * sign if (isEmpty) null else res * sign
} }
} }

View File

@ -13,7 +13,7 @@ import kotlin.test.assertNull
class BigIntConversionsTest { class BigIntConversionsTest {
@Test @Test
fun emptyString() { fun testEmptyString() {
assertNull("".parseBigInteger()) assertNull("".parseBigInteger())
assertNull("+".parseBigInteger()) assertNull("+".parseBigInteger())
assertNull("-".parseBigInteger()) assertNull("-".parseBigInteger())
@ -38,6 +38,12 @@ class BigIntConversionsTest {
assertEquals("0x10", x.toString()) assertEquals("0x10", x.toString())
} }
@Test
fun testUnderscores() {
assertEquals("0x10", "0x_1_0_".parseBigInteger().toString())
assertEquals("0xa", "_1_0_".parseBigInteger().toString())
}
@Test @Test
fun testToString0x17ffffffd() { fun testToString0x17ffffffd() {
val x = 0x17ffffffdL.toBigInt() val x = 0x17ffffffdL.toBigInt()