【问题标题】:Java Double Trouble - How can I ensure 4.45 is 4.45 without using BigDecimal? [duplicate]Java Double Trouble - 如何在不使用 BigDecimal 的情况下确保 4.45 是 4.45? [复制]
【发布时间】:2023-10-10 07:45:01
【问题描述】:

所以:

//aValue = 4.45
//bValue = 4.35
//maxValueDiff = 0.1
//The absolute value of a - b = 0.1

if( Math.abs(aValue - bValue) <= maxValueDiff ) return true;
logger.info("valueDiffCheck a:" + aValue +" b:"+ bValue + " e1:" + maxValueDiff);

为什么我会在我的日志中看到这个:

valueDiffCheck a:4.45 b:4.35 e1:0.1

怀疑这是因为 Double 是不准确的双重痛苦,但如果是这种情况,我不应该看到吗?喜欢它应该打印 0.1 实际上是 0.10000000111 还是什么? ...或者打印格式有什么讨厌的地方?

目标代码具有大量数据的高性能,因此我们不想使用 BigDecimal。确保 0.44 是 0.44 的下一个最佳方法是什么?

好的,让我明确声明我们只需要两位小数。使用 BigDecimal 所涉及的准确性是主要的矫枉过正。

【问题讨论】:

  • 如果您想要更高的精度,则必须使用更大的小数来比较。减法和加法虽然不是问题。如果你担心精度问题,你应该更关心除法。
  • @Luminous,“不知道“减法和加法不是问题”是什么意思。 c 不等于实数 c',即 a 和 b 的实际总和。c 和 c' 之间的差值可能高达一个 ULP 的 1/2。
  • 实际上,它打印正确。你应该打印的是 Math.abs(aValue - bValue)
  • 写了一个简短的单元测试,这是导致不准确的数学。我只需要两位小数。结果证明是愚蠢的,比如 0.1000000000000123
  • 请阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic。这个问题每周在 SO 上出现几次。简单的答案是,不使用精确的十进制算术(即BigDecimal)是不可能的。

标签: java double rounding truncate


【解决方案1】:

我通过在可能出现的地方使用误差范围来解决这个问题。以下是一种非常简单的方法 - 但不一定是最好的方法(您可能希望它更集中、可配置等) - 修改您的代码以执行此操作:

private static final double ERROR = 1e-7;

// SNIP

if( Math.abs(aValue - bValue) <= maxValueDiff + ERROR) return true;
logger.info("valueDiffCheck a:" + aValue +" b:"+ bValue + " e1:" + maxValueDiff);

【讨论】:

  • 不错的...现在找到所有可以插入的地方 ;)
  • @Drejc 最好的办法是使用像“DoubleChecker”这样的方法——你甚至可以使用自定义的Hamcrest 匹配器——但所有这些都比这个问题的范围更复杂。
【解决方案2】:

如果你想准确一点,我看不出有什么办法可以使用 BigDecimal。

但根据您对这些数字的处理方式,您始终可以使用 Math.round 进行四舍五入,以“确保”您设置了“正确”的小数。

Round a double to 2 decimal places

【讨论】:

  • 上述问题是关于如何避免 BigDecimal。提供的解决方案使用 BigDecimal。我们只需要 2 位小数。
  • 如果您阅读了答案...以及链接问题中的答案,解决方案是使用 Math.round。
【解决方案3】:

您始终可以考虑使用自己的十进制类,该类使用long 来存储值和小数占位符。

public Class MyDecimal
{
    private long value = 0;
    private long decimalPlace = 0;

    ...
}

这意味着您可以让 0.44 执行以下操作:

MyDecimal decimal = new MyDecimal(44, 2);

正如另一个答案中提到的那样,BigDecimal 将是确保准确性的最佳选择。

【讨论】:

    最近更新 更多