【问题标题】:'Practical' double rounding in JavaJava中的“实用”双舍入
【发布时间】:2015-08-11 18:24:29
【问题描述】:

所以我已经阅读了很多这方面的内容,所以请知道我知道像 0.6 这样的数字不能绝对准确地表示为 Java 双精度 - 但我知道有一个双精度版本代表数字 0.6 足够接近,以至于数字显示为 0.6 而不是像 0.6000000000000001 在该数字的大多数最终解释中(例如,包括 toString() 或使用像杰克逊这样的库封送到 JSON - 我的目标)。

我正在寻找一种将双精度数强制转换为被解释为相对截断精度的数字的机制。例如,如果我想截断到 0.1 精度,我想要将 0.6975265613 截断到 0.1 精度的东西为 0.6,而不是 0.6000000000000001

所以,基本上使以下测试工作:

@Test
public void testRounding() {

    // prove there is a value that displays cleanly
    Double cleanValue = 0.6d;
    assertEquals("0.6", cleanValue.toString());

    Double value = 0.6975265613d;
    Double precision = 0.1d;

    value = value / precision;
    value = value.longValue() * precision;

    // this fails with 0.6000000000000001
    assertEquals("0.6", value.toString());

}

我使用 longValue() 来截断速度,但使用 Math.floor() 会得到相同的结果。

查看原始双十六进制值,上面的 cleanValue 是: 3fe3333333333333

而四舍五入的结果(值)为: 3fe3333333333334

我需要一种能够始终如一地为我提供预期版本(第一个)的技术,而不管舍入精度如何 - 假设精度不会突破类型的精度限制。转换为 String 或 BigDecimal 会太慢 - 这是一个分析应用程序,其中这些方法的 x10 成本非常可衡量。

再次,理解这两个十六进制数字都不是真正代表 实际 数字 0.6,我只是想让 Java 认为它是 0.6(即 3fe3333333333333):)

谢谢!

【问题讨论】:

  • 所以你要做的是找到整数xy 使得x / 10^y 尽可能接近双倍。所以这是一个二维优化问题。
  • 有什么理由不想使用 BigDecimal?
  • 我建议不要将精度存储为 0.1d - 您在那里遇到了一个很容易避免的舍入误差。
  • 我认为这没有任何实际意义。有太多方法可以将双精度转换为十进制,以使单一技术在双端可靠。您应该关注的是转换过程本身,它易于控制,并确保您始终使用它。
  • 我不知道,对我来说似乎足够实用。我们要求 +/ 这样的运算正确舍入;为什么不要求同样的舍入?这不像exp,在一个 ULP 内有一个令人信服的理由来满足准确性。

标签: java rounding precision point floating


【解决方案1】:

这通过了,但不确定它是否是你想要的

@Test
public void testRounding() {

    // prove there is a value that displays cleanly
    Double cleanValue = 0.6d;
    assertEquals("0.6", cleanValue.toString());

    Double value = 0.6975265613d;

    // get double value truncated to 1 decimal place
    value = BigDecimal.valueOf(value).setScale(1,BigDecimal.ROUND_DOWN).doubleValue();

    // this fails with 0.6000000000000001
    assertEquals("0.6", value.toString());

}

【讨论】:

  • 感谢您的回复,但正如我所说,BigDecimal 太慢了。例如:运行我的原始转换需要 17 毫秒进行 1 亿次迭代,而 BigDecimal 方法在我的机器上需要 47,333 毫秒……所以更像是 3000 倍的惩罚而不是 100 倍的惩罚。就是行不通。
  • @tsquared:整理数据并将其写入磁盘或通过网络发送或其他方式需要多长时间?
  • @tsquared:另外,您是否考虑过为 Jackson 提供您自己的双打自定义序列化程序?
  • 我意识到我有一点独特的需要来运行数百万条记录并对数据点进行分箱 - 所以无论结果如何编组,我都需要高速舍入。将值四舍五入到 Java 认为的 0.6 而不是一点点会更优雅,但是经过大量算法尝试预测所有值的值(尝试阅读 Sun 的 FloatingPointDecimal.dtoa()功能) - 我意识到它几乎无法辨认。如果我能让分箱始终达到 0.6000..001,那么我可以在编组时对其进行清理。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多