【问题标题】:Groovy BigDecimal Precision ProblemGroovy BigDecimal 精度问题
【发布时间】:2011-06-13 06:27:30
【问题描述】:

给定以下行

BigDecimal step = 10.0G**-1.0G

groovy 1.7.5 返回错误

0.1000000000000000055511151231257827021181583404541015625

而 groovy 1.8 返回正确的

0.1

不幸的是,我想在 Grails 中解决我的问题。带有 groovy 的 1.4 1.8 还不够稳定(控制器不会在开发模式下刷新),而 grails 1.3.7 带有 groovy 1.7.x

两个问题:

  • 是我做错了什么还是 1.7.5 中的错误?

  • 如何避免这种行为?我认为 BigDecimals 在哪里最适合这种舍入问题?

第二次更新:(忘记第一次更新)-;

我现在有点困惑。似乎每次尝试都会得到不同的结果......:

BigDecimal step = 10.0G**-1.0G
println step

返回0.1000000000000000055511151231257827021181583404541015625

和

println 10.0G**-1.0G

返回

0.1

在两个 groovy 版本中。

但是,如果您只是将BigDec step = 10.0G**-1.0G 放在 groovyConsole 中并让控制台打印最后的响应,您会得到不同 groovy 版本的不同结果。所以一个问题似乎是 groovyConsole。

另一个问题似乎是执行的 toString 转换。

而且似乎涉及到一些自动装箱......当我做一个

def step = 10.0G**-1.0G

结果是一个 Double...

我想这可以将问题归结为两个问题:

  • a) 哪些数学运算是 BigDecimal 运算?

和

  • b) 如何轻松地对 BigDecimal 进行四舍五入,以便更正上述问题?

感谢您的耐心

【问题讨论】:

  • 如果只是在 groovy 控制台中,还是有问题?
  • 是的。主要问题是我的计算遇到了那些舍入问题。我虽然已经用上面的例子追踪了它。我会尝试再次追踪它。请继续关注... :-)

标签: groovy bigdecimal


【解决方案1】:

我想我明白了。这是来自 java doc 的线索:

BigDecimal(double val) 将 double 转换为 BigDecimal,即 exact 小数 双精度二进制的表示 浮点值。

  • 因此,我在代码中使用的 Math.-Functions 似乎不支持 BigDecimal。
  • 结果在后台从 Double 转换为 BigDecimal
  • 当双精度为 0.1 时,双精度二进制的 精确 表示 是 0.1000000000000000055511151231257827021181583404541015625
  • println 10.0G**-1.0G 打印双精度值 (0.1)
  • BigDecimal step = 10.0G**-1.0G; println step 打印双精度 0.1 的 BigDecimal 表示,这是上面的丑数

似乎 groovy 版本的行为(就 BigDecimals 而言)没有区别,但结果在 groovyConsole 中的输出方式有所不同。

【讨论】:

    【解决方案2】:

    我正在使用Groovy 1.7.5,对我来说10.0G**-1.0G 返回0.1

    你在哪个 JDK 上运行?

    【讨论】:

    • 嗯。两个 groovyConsole 都从环境变量中读取为 JAVA_HOME jdk1.6.0_21...但BigDecimal step = 10.0G**-1.0G; println step 和println 10.0G**-1.0G 之间似乎存在差异...
    【解决方案3】:

    我怀疑 10.0G**-1.0G 会变成 M​​ath.pow(10, -1),math.pow 返回一个双精度数,然后给出舍入误差。 为避免这种情况,如果您只提高到 -1,您可以将其实现为除法 (1.0G/10.0G)。

    也就是说,我不太了解 Groovy,所以我的猜测可能是错误的,但这肯定是它看起来的样子,而且它得到了 Groovy 文档所说的支持。

    【讨论】:

    • 在这个例子中用更简单的代码替换数学函数是个好主意。不幸的是,我的代码使用了更多的数学函数(例如日志)。是否有特殊的 BigMath 函数?
    • 不在我所知道的标准 java 中。您需要多少精度?如果它小于 16 位,则可以使用双打。如果更多,您需要考虑使用外部库或编写自己的库。在编写您自己的 BigDecimal 实现时,有一些关于堆栈溢出的问题,例如 log
    • 这与精度无关。我更想避免那些舍入错误...... - 不容易解释。这是绘制图表轴的代码。
    • BigDecimal 支持pow 方法,但它只排除int - 所以我不知道如果你将它传递给BigDecimal 它将如何表现。您应该尝试在 Groovy 的规范中查找它 - 我没有发现任何关于它的信息
    • BigDecimal step = 10**-1; println step 看起来仍然很丑... :-(
    【解决方案4】:

    如果您想从 Double 转换为 BigDecimal 并且不获取所有额外的数字,一种方法是将 Double 放入 String,然后将其转换为 BigDecimal,这将防止所有额外的数字出现来自双精度的精确二进制表示。

    Double someFraction = 1.23456789
    BigDecimal sameFraction = "${someFraction}".toBigDecimal()
    

    【讨论】:

      猜你喜欢
      • 2013-03-05
      • 1970-01-01
      • 2020-07-28
      • 2016-05-27
      • 2015-03-04
      • 2012-07-18
      • 1970-01-01
      • 2012-09-05
      • 1970-01-01
      相关资源
      最近更新 更多