我在 Scalacheck 中发现了一个问题,它会arbitrary[BigDecimal]
生成BigDecimal
无法转换为String
s 然后再转换为 sBigDecimal
的 s,我正在尝试与创建者一起寻找解决方案,但我不确定MathContext
s 是如何来的玩。
原始生成器如下所示:
/** Arbitrary BigDecimal */
implicit lazy val arbBigDecimal: Arbitrary[BigDecimal] = {
import java.math.MathContext._
val mcGen = oneOf(UNLIMITED, DECIMAL32, DECIMAL64, DECIMAL128)
val bdGen = for {
x <- arbBigInt.arbitrary
mc <- mcGen
limit <- const(if(mc == UNLIMITED) 0 else math.max(x.abs.toString.length - mc.getPrecision, 0))
scale <- Gen.chooseNum(Int.MinValue + limit , Int.MaxValue)
} yield {
try {
BigDecimal(x, scale, mc)
} catch {
case ae: java.lang.ArithmeticException => BigDecimal(x, scale, UNLIMITED) // Handle the case where scale/precision conflict
}
}
Arbitrary(bdGen)
}
问题在于BigDecimal
使用的构造函数反转了scale
参数的符号,从而使Int.MinValue
变成scale
大于 2^32 -1。
scala> val orig = BigDecimal(BigInt("-28334198897217871282176"), -2147483640, UNLIMITED)
orig: scala.math.BigDecimal = -2.8334198897217871282176E+2147483662
scala> BigDecimal(orig.toString)
java.lang.NumberFormatException
at java.math.BigDecimal.<init>(BigDecimal.java:554)
at java.math.BigDecimal.<init>(BigDecimal.java:383)
at java.math.BigDecimal.<init>(BigDecimal.java:806)
at scala.math.BigDecimal$.exact(BigDecimal.scala:125)
at scala.math.BigDecimal$.apply(BigDecimal.scala:283)
... 33 elided
修复的核心是通过 中的位数增加下限unscaledVal
,但我只是想到了一种方法来做到这一点MathContext.UNLIMITED
。如果我们这样做,我担心我们会错过生成器的鲁棒性:
lazy val genBigDecimal: Gen[BigDecimal] = for {
unscaledVal <- arbitrary[BigInt]
scale <- Gen.chooseNum(Int.MinValue + unscaledVal.abs.toString.length, Int.MaxValue)
} yield BigDecimal(unscaledVal, scale)
那么,如果我们想继续使用其他MathContext
的,我们必须做些什么来确保我们正确地使用它们呢?