8000 Use JS bigint's if possible inside the `parseFloat` algorithm. · scala-js/scala-js@f0e7a33 · GitHub
[go: up one dir, main page]

Skip to content

Commit f0e7a33

Browse files
committed
Use JS bigint's if possible inside the parseFloat algorithm.
We use a `linkTimeIf` to select a `bigint`-based implementation of `parseFloatDecimalCorrection` when they are supported. We need a `linkTimeIf` in this case because it uses the JS `**` operator, which does not link below ES 2016. The `bigint`-based implementation avoids bringing in the entire `BigInteger` implementation, which is a major code size win if that was the only reason `BigInteger` was needed.
1 parent 5e842d8 commit f0e7a33

File tree

1 file changed

+69
-13
lines changed

1 file changed

+69
-13
lines changed

javalib/src/main/scala/java/lang/Float.scala

Lines changed: 69 additions & 13 deletions
< 67ED td data-grid-cell-id="diff-bac3ba881ae5c65c4db1a2daaab7bbe0b539b09911076b5df246d5fa3a9c4231-300-345-0" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-additionNum-bgColor, var(--diffBlob-addition-bgColor-num));text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative left-side">
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
package java.lang
1414

1515
import java.lang.constant.{Constable, ConstantDesc}
16-
import java.math.BigInteger
1716

1817
import scala.scalajs.js
18+
import scala.scalajs.LinkingInfo._
1919

2020
/* This is a hijacked class. Its instances are primitive numbers.
2121
* Constructors are not emitted.
@@ -226,9 +226,23 @@ object Float {
226226
fractionalPartStr: String, exponentStr: String,
227227
zDown: scala.Float, zUp: scala.Float, mid: scala.Double): scala.Float = {
228228

229+
/* Get the best available implementation of big integers for the given platform.
230+
*
231+
* If JS bigint's are supported, use them. Otherwise fall back on
232+
* `java.math.BigInteger`.
233+
*
234+
* We need a `linkTimeIf` here because the JS bigint implementation uses
235+
* the `**` operator, which does not link when `esVersion < ESVersion.ES2016`.
236+
*/
237+
val bigIntImpl = linkTimeIf[BigIntImpl](esVersion >= ESVersion.ES2020) {
238+
BigIntImpl.JSBigInt
239+
} {
240+
BigIntImpl.JBigInteger
241+
}
242+
229243
// 1. Accurately parse the string with the representation f × 10ᵉ
230244

231-
val f: BigInteger = new BigInteger(integralPartStr + fractionalPartStr)
245+
val f: bigIntImpl.Repr = bigIntImpl.fromString(integralPartStr + fractionalPartStr)
232246
val e: Int = Integer.parseInt(exponentStr) - fractionalPartStr.length()
233247

234248
/* Note: we know that `e` is "reasonable" (in the range [-324, +308]). If
@@ -261,24 +275,23 @@ object Float {
261275

262276
val mExplicitBits = midBits & ((1L << mbits) - 1)
263277
val mImplicit1Bit = 1L << mbits // the implicit '1' bit of a normalized floating-point number
264-
val m = BigInteger.valueOf(mExplicitBits | mImplicit1Bit)
278+
val m = bigIntImpl.fromUnsignedLong53(mExplicitBits | mImplicit1Bit)
265279
val k = biasedK - bias - mbits
266280

267281
// 3. Accurately compare f × 10ᵉ to m × 2ᵏ
268282

269-
@inline def compare(x: BigInteger, y: BigInteger): Int =
270-
x.compareTo(y)
283+
import bigIntImpl.{multiplyBy2Pow, multiplyBy10Pow}
271284

272285
val cmp = if (e >= 0) {
273286
if (k >= 0)
274-
compare(multiplyBy10Pow(f, e), multiplyBy2Pow(m, k))
287+
bigIntImpl.compare(multiplyBy10Pow(f, e), multiplyBy2Pow(m, k))
275288
else
276-
compare(multiplyBy2Pow(multiplyBy10Pow(f, e), -k), m) // this branch may be dead code in practice
289+
bigIntImpl.compare(multiplyBy2Pow(multiplyBy10Pow(f, e), -k), m) // this branch may be dead code in practice
277290
} else {
278291
if (k >= 0)
279-
compare(f, multiplyBy2Pow(multiplyBy10Pow(m, -e), k))
292+
bigIntImpl.compare(f, multiplyBy2Pow(multiplyBy10Pow(m, -e), k))
280293
else
281-
compare(multiplyBy2Pow(f, -k), multiplyBy10Pow(m, -e))
294+
bigIntImpl.compare(multiplyBy2Pow(f, -k), multiplyBy10Pow(m, -e))
282295
}
283296

284297
// 4. Choose zDown or zUp depending on the result of the comparison
@@ -293,11 +306,54 @@ object Float {
293306
zUp
294307
}
295308

296-
@inline private def multiplyBy10Pow(v: BigInteger, e: Int): BigInteger =
297-
v.multiply(BigInteger.TEN.pow(e))
309+
/** An implementation of big integer arithmetics that we need in the above method. */
310+
private sealed abstract class BigIntImpl {
311+
type Repr
312+
313+
def fromString(str: String): Repr
314+
315+
/** Creates a big integer from a `Long` that needs at most 53 bits (unsigned). */
316+
def fromUnsignedLong53(x: scala.Long): Repr
317+
318+
def multiplyBy2Pow(v: Repr, e: Int): Repr
319+
def multiplyBy10Pow(v: Repr, e: Int): Repr
320+
321+
def compare(x: Repr, y: Repr): Int
322+
}
323+
324+
private object BigIntImpl {
325+
object JSBigInt extends BigIntImpl {
326+
type Repr = js.BigInt
327+
328+
@inline def fromString(str: String): Repr = js.BigInt(str)
298329

299-
@inline private def multiplyBy2Pow(v: BigInteger, e: Int): BigInteger =
300-
v.shiftLeft(e)
330+
// The 53-bit restriction guarantees that the conversion to `Double` is lossless.
331+
@inline def fromUnsignedLong53(x: scala.Long): Repr = js.BigInt(x.toDouble)
332+
333+
@inline def multiplyBy2Pow(v: Repr, e: Int): Repr = v << js.BigInt(e)
334+
@inline def multiplyBy10Pow(v: Repr, e: Int): Repr = v * (js.BigInt(10) ** js.BigInt(e))
335+
336+
@inline def compare(x: Repr, y: Repr): Int = {
337+
if (x < y) -1
338+
else if (x > y) 1
339+
else 0
340+
}
341+
}
342+
343+
object JBigInteger extends BigIntImpl {
344+
import java.math.BigInteger
345+
346+
type Repr = BigInteger
347+
348+
@inline def fromString(str: String): Repr = new BigInteger(str)
349+
@inline def fromUnsignedLong53(x: scala.Long): Repr = BigInteger.valueOf(x)
350+
351+
@inline def multiplyBy2Pow(v: Repr, e: Int): Repr = v.shiftLeft(e)
352+
@inline def multiplyBy10Pow(v: Repr, e: Int): Repr = v.multiply(BigInteger.TEN.pow(e))
353+
354+
@inline def compare(x: Repr, y: Repr): Int = x.compareTo(y)
355+
}
356+
}
301357

302358
private def parseFloatHexadecimal(integralPartStr: String,
303359
fractionalPartStr: String, binaryExpStr: String): scala.Float = {

0 commit comments

Comments
 (0)
0