如何解决 Java 四舍五入问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/179427/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 10:57:24  来源:igfitidea点击:

How to resolve a Java Rounding Double issue

javamathrounding

提问by Patrick

Seems like the subtraction is triggering some kind of issue and the resulting value is wrong.

似乎减法引发了某种问题,结果值是错误的。

double tempCommission = targetPremium.doubleValue()*rate.doubleValue()/100d;

78.75 = 787.5 * 10.0/100d

78.75 = 787.5 * 10.0/100d

double netToCompany = targetPremium.doubleValue() - tempCommission;

708.75 = 787.5 - 78.75

708.75 = 787.5 - 78.75

double dCommission = request.getPremium().doubleValue() - netToCompany;

877.8499999999999 = 1586.6 - 708.75

877.8499999999999 = 1586.6 - 708.75

The resulting expected value would be 877.85.

结果预期值为 877.85。

What should be done to ensure the correct calculation?

应该怎么做才能确保计算正确?

采纳答案by Eric Weilnau

To control the precision of floating point arithmetic, you should use java.math.BigDecimal. Read The need for BigDecimalby John Zukowski for more information.

要控制浮点运算的精度,您应该使用java.math.BigDecimal。阅读John Zukowski 对BigDecimal 的需求以获取更多信息。

Given your example, the last line would be as following using BigDecimal.

鉴于您的示例,最后一行将使用 BigDecimal 如下所示。

import java.math.BigDecimal;

BigDecimal premium = BigDecimal.valueOf("1586.6");
BigDecimal netToCompany = BigDecimal.valueOf("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

This results in the following output.

这导致以下输出。

877.85 = 1586.6 - 708.75

回答by Adam Bellaire

See responses to this question. Essentially what you are seeing is a natural consequence of using floating point arithmetic.

请参阅对此问题的答复。本质上,您所看到的是使用浮点运算的自然结果。

You could pick some arbitrary precision (significant digits of your inputs?) and round your result to it, if you feel comfortable doing that.

如果您觉得这样做很舒服,您可以选择一些任意精度(输入的有效数字?)并将结果四舍五入。

回答by Steve Klabnik

Any time you do calculations with doubles, this can happen. This code would give you 877.85:

任何时候用双打进行计算时,都可能发生这种情况。此代码将为您提供 877.85:

double answer = Math.round(dCommission * 100000) / 100000.0;

double answer = Math.round(dCommission * 100000) / 100000.0;

回答by tloach

Save the number of cents rather than dollars, and just do the format to dollars when you output it. That way you can use an integer which doesn't suffer from the precision issues.

保存美分而不是美元的数量,输出时只需将格式设置为美元即可。这样你就可以使用一个不受精度问题影响的整数。

回答by toolkit

Another example:

另一个例子:

double d = 0;
for (int i = 1; i <= 10; i++) {
    d += 0.1;
}
System.out.println(d);    // prints 0.9999999999999999 not 1.0

Use BigDecimal instead.

请改用 BigDecimal。

EDIT:

编辑:

Also, just to point out this isn't a 'Java' rounding issue. Other languages exhibit similar (though not necessarily consistent) behaviour. Java at least guarantees consistent behaviour in this regard.

另外,只是要指出这不是“Java”四舍五入问题。其他语言表现出类似(虽然不一定一致)的行为。Java 至少在这方面保证了一致的行为。

回答by Johann Zacharee

As the previous answers stated, this is a consequence of doing floating point arithmetic.

正如前面的答案所述,这是进行浮点运算的结果。

As a previous poster suggested, When you are doing numeric calculations, use java.math.BigDecimal.

正如之前的海报所建议的那样,在进行数值计算时,请使用java.math.BigDecimal.

However, there is a gotcha to using BigDecimal. When you are converting from the double value to a BigDecimal, you have a choice of using a new BigDecimal(double)constructor or the BigDecimal.valueOf(double)static factory method. Use the static factory method.

但是,使用BigDecimal. 当您从 double 值转换为 a 时BigDecimal,您可以选择使用新的BigDecimal(double)构造函数或BigDecimal.valueOf(double)静态工厂方法。使用静态工厂方法。

The double constructor converts the entire precision of the doubleto a BigDecimalwhile the static factory effectively converts it to a String, then converts that to a BigDecimal.

double 构造函数将 the 的整个精度转换double为 aBigDecimal而静态工厂有效地将其转换为 a String,然后将其转换为 a BigDecimal

This becomes relevant when you are running into those subtle rounding errors. A number might display as .585, but internally its value is '0.58499999999999996447286321199499070644378662109375'. If you used the BigDecimalconstructor, you would get the number that is NOT equal to 0.585, while the static method would give you a value equal to 0.585.

当您遇到那些细微的舍入错误时,这变得很重要。一个数字可能显示为 .585,但在内部它的值是“0.58499999999999996447286321199499070644378662109375”。如果你使用BigDecimal构造函数,你会得到不等于 0.585 的数字,而静态方法会给你一个等于 0.585 的值。

double value = 0.585;
System.out.println(new BigDecimal(value));
System.out.println(BigDecimal.valueOf(value));

on my system gives

在我的系统上给出

0.58499999999999996447286321199499070644378662109375
0.585

回答by Johann Zacharee

I would modify the example above as follows:

我将修改上面的例子如下:

import java.math.BigDecimal;

BigDecimal premium = new BigDecimal("1586.6");
BigDecimal netToCompany = new BigDecimal("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

This way you avoid the pitfalls of using string to begin with. Another alternative:

这样您就可以避免使用字符串开头的陷阱。另一种选择:

import java.math.BigDecimal;

BigDecimal premium = BigDecimal.valueOf(158660, 2);
BigDecimal netToCompany = BigDecimal.valueOf(70875, 2);
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

I think these options are better than using doubles. In webapps numbers start out as strings anyways.

我认为这些选项比使用双打更好。在 webapps 中,数字无论如何都是从字符串开始的。

回答by Roman Kagan

So far the most elegant and most efficient way to do that in Java:

到目前为止,在 Java 中最优雅、最有效的方法是:

double newNum = Math.floor(num * 100 + 0.5) / 100;

回答by Denis

double rounded = Math.rint(toround * 100) / 100;

回答by Timon

Although you should not use doubles for precise calculations the following trick helped me if you are rounding the results anyway.

尽管您不应该使用双精度数进行精确计算,但如果您对结果进行四舍五入,以下技巧对我有所帮助。

public static int round(Double i) {
    return (int) Math.round(i + ((i > 0.0) ? 0.00000001 : -0.00000001));
}

Example:

例子:

    Double foo = 0.0;
    for (int i = 1; i <= 150; i++) {
        foo += 0.00010;
    }
    System.out.println(foo);
    System.out.println(Math.round(foo * 100.0) / 100.0);
    System.out.println(round(foo*100.0) / 100.0);

Which prints:

哪个打印:

0.014999999999999965
0.01
0.02

More info: http://en.wikipedia.org/wiki/Double_precision

更多信息:http: //en.wikipedia.org/wiki/Double_precision