BigDecimals中的MathContexts - ScalaCheck生成器创建BigDecimals,这些BigDecimals无法序列化然后反序列化。如何正确使用MathContexts?

时间:2014-12-11 23:02:39

标签: scala precision bigdecimal scalacheck mathcontext

我发现an issue in Scalacheck arbitrary[BigDecimal]生成BigDecimal无法转换为String s然后又转回BigDecimal s,而我{&#}} 39;我试图与创作者合作找到它的修复方法,但我不确定MathContext是如何发挥作用的。

original generator看起来像这样:

/** 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 constructor used会反转scale参数的符号,从而使Int.MinValue变成大于2 ^ 32 -1的scale

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,我们需要做些什么来确保我们正确使用它们?

0 个答案:

没有答案