简体   繁体   English

如何“修复” java 中的浮点错误?

[英]How can I "fix" floating point errors in java?

I realize that the real answer is "don't use floating point numbers, use BigDecimal" but that will be a longer-term change in a large codebase.我意识到真正的答案是“不要使用浮点数,使用 BigDecimal”,但这将是大型代码库中的长期变化。

My problem is relatively simple - I have numbers (doubles), both positive and negative, that I want to round towards zero to the nearest whole number.我的问题相对简单——我有正数和负数的数字(双精度数),我想将其四舍五入到最接近的整数。 However, in the case of floating point imprecision errors, I need them to be rounded to the "actual" number.但是,在浮点不精确错误的情况下,我需要将它们四舍五入为“实际”数字。

Examples:例子:

  • 351.365 rounds to 351 351.365 轮到 351
  • 3.9991532 rounds to 3 3.9991532 轮到 3
  • 3.9999999999999996 rounds to 4 3.9999999999999996 舍入到 4
  • -4.5 rounds to -4 -4.5 轮到 -4
  • -123.9999999999993 rounds to -124 -123.9999999999993 舍入为 -124

I guess for the purposes of this question, we can just ignore the fact that the rounding errors are caused by floating point imprecision, and rephrase the requirements as:我想出于这个问题的目的,我们可以忽略舍入误差是由浮点不精确引起的这一事实,并将要求改写为:

"Round towards zero to the nearest whole number, unless the value is less than 1e-6 away from a whole number N, in which case round to N." “向零舍入到最接近的整数,除非该值距离整数 N 小于 1e-6,在这种情况下舍入到 N。”

My code is gross (particularly the "% 1" feels wrong, and I know that there could be a loss of precision casting double to int, but for simplicity just assume that the double values are all less than Integer.MAX_VALUE):我的代码很糟糕(特别是“% 1”感觉不对,而且我知道将 double 转换为 int 可能会丢失精度,但为简单起见,假设 double 值都小于 Integer.MAX_VALUE):

private int getRoundedValue(double d) {
    double fractionalPart = d % 1;
    if (Math.abs(fractionalPart) < 1e-6 || (1 - Math.abs(fractionalPart) < 1e-6)) {
        
        // here we are close enough to a whole number so just round to it (up or down)
        return (int) Math.round(d);
    }

    // otherwise just return the nearest whole number towards zero
    return (int) d;  
} 

How can I improve the above method?如何改进上述方法?

Here's a simpler version:这是一个更简单的版本:

private static int getRoundedValue(double d) {
    return Math.toIntExact((long)(Math.round(d * 1e6) / 1e6));
}

Math.toIntExact throws ArithmeticException on overflows. Math.toIntExact在溢出时抛出ArithmeticException

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM