171
public class doublePrecision {
    public static void main(String[] args) {

        double total = 0;
        total += 5.6;
        total += 5.8;
        System.out.println(total);
    }
}

上面的代码打印:

11.399999999999

我如何才能让它打印(或能够将其用作)11.4?

4

23 回答 23

166

BigDecimal正如其他人所提到的,如果您想获得 11.4 的精确表示,您可能想要使用该类。

现在,稍微解释一下为什么会发生这种情况:

Java 中的floatdouble原始类型是浮点数,其中数字存储为分数和指数的二进制表示。

更具体地说,类型等双精度浮点值double是 64 位值,其中:

  • 1 位表示符号(正或负)。
  • 11 位为指数。
  • 有效数字为 52 位(小数部分为二进制)。

这些部分组合起来产生一个double值的表示。

(来源:维基百科:双精度

有关如何在 Java 中处理浮点值的详细说明,请参阅第 4.2.3 节:Java 语言规范的浮点类型、格式和值

, byte, char,类型是定点数int,它们是数字的精确表示。与定点数不同,浮点数有时(可以安全地假设“大部分时间”)无法返回数字的精确表示。这就是为什么你最终得到.long11.3999999999995.6 + 5.8

当需要一个精确的值(例如 1.5 或 150.1005)时,您需要使用其中一种能够精确表示数字的定点类型。

正如已经多次提到的,Java 有一个BigDecimal类可以处理非常大的数字和非常小的数字。

来自BigDecimal该类的 Java API 参考:

不可变的、任意精度的有符号十进制数。BigDecimal 由一个任意精度的整数未缩放值和一个 32 位整数刻度组成。如果为零或正数,则刻度是小数点右侧的位数。如果为负数,则将数字的未缩放值乘以 10 的负数次方。因此 BigDecimal 表示的数字的值是 (unscaledValue × 10^-scale)。

Stack Overflow 上有很多关于浮点数及其精度的问题。以下是可能感兴趣的相关问题列表:

如果您真的想深入了解浮点数的基本细节,请查看每个计算机科学家应该了解的关于浮点运算的知识

于 2008-11-27T03:27:46.780 回答
108

当你输入一个双精度数时,例如 ,33.33333333333333你得到的值实际上是最接近的可表示的双精度值,也就是:

33.3333333333333285963817615993320941925048828125

将其除以 100 得出:

0.333333333333333285963817615993320941925048828125

这也不能表示为双精度数,因此再次将其四舍五入为最接近的可表示值,即:

0.3333333333333332593184650249895639717578887939453125

当您打印出这个值时,它会再次四舍五入到 17 位十进制数字,给出:

0.33333333333333326
于 2010-03-18T00:41:56.650 回答
24

如果您只想将值处理为分数,您可以创建一个包含分子和分母字段的 Fraction 类。

编写加法、减法、乘法和除法的方法以及 toDouble 方法。这样您就可以在计算过程中避免浮动。

编辑:快速实施,

public class Fraction {

private int numerator;
private int denominator;

public Fraction(int n, int d){
    numerator = n;
    denominator = d;
}

public double toDouble(){
    return ((double)numerator)/((double)denominator);
}


public static Fraction add(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop + bTop, a.denominator * b.denominator);
    }
    else{
        return new Fraction(a.numerator + b.numerator, a.denominator);
    }
}

public static Fraction divide(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator);
}

public static Fraction multiply(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator);
}

public static Fraction subtract(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop-bTop, a.denominator*b.denominator);
    }
    else{
        return new Fraction(a.numerator - b.numerator, a.denominator);
    }
}

}
于 2010-03-18T01:09:06.223 回答
16

请注意,如果您使用有限精度的十进制算术,并且想要处理 1/3:0.333333333 * 3 是 0.999999999,而不是 1.00000000,则会遇到同样的问题。

不幸的是,5.6、5.8 和 11.4 并不是二进制的整数,因为它们涉及五度。所以它们的浮点表示并不精确,就像 0.3333 不完全是 1/3 一样。

如果您使用的所有数字都是非循环小数,并且您想要精确的结果,请使用 BigDecimal。或者正如其他人所说,如果你的值就像钱一样,它们都是 0.01 或 0.001 的倍数,然后将所有值乘以 10 的固定幂并使用 int 或 long(加法和减法是琐碎的:注意乘法)。

但是,如果您对二进制计算感到满意,但只想以稍微友好的格式打印出来,请尝试使用java.util.FormatterString.format. 在格式字符串中指定的精度小于双精度的完整精度。对于 10 位有效数字,例如 11.399999999999 是 11.4,因此在二进制结果非常接近只需要几个小数位的值的情况下,结果几乎一样准确且更易于阅读。

指定的精度在一定程度上取决于您对数字进行了多少数学运算-通常,您做的越多,累积的错误就越多,但是某些算法的累积速度比其他算法快得多(它们被称为“不稳定”)与舍入误差的“稳定”相反)。如果您所做的只是添加一些值,那么我猜只删除小数点后一位就可以解决问题。实验。

于 2008-11-27T03:11:07.640 回答
9

如果您真的需要精确数学,您可能需要考虑使用 java.math.BigDecimal 类。这是 Oracle/Sun 关于BigDecimal 案例的一篇好文章。虽然你永远不能像有人提到的那样代表 1/3,但你可以决定你想要的结果有多精确。setScale() 是你的朋友.. :)

好的,因为目前我手头有太多时间,这是一个与您的问题相关的代码示例:

import java.math.BigDecimal;
/**
 * Created by a wonderful programmer known as:
 * Vincent Stoessel
 * xaymaca@gmail.com
 * on Mar 17, 2010 at  11:05:16 PM
 */
public class BigUp {

    public static void main(String[] args) {
        BigDecimal first, second, result ;
        first = new BigDecimal("33.33333333333333")  ;
        second = new BigDecimal("100") ;
        result = first.divide(second);
        System.out.println("result is " + result);
       //will print : result is 0.3333333333333333


    }
}

并插入我最喜欢的新语言 Groovy,这是同一事物的一个更简洁的示例:

import java.math.BigDecimal

def  first =   new BigDecimal("33.33333333333333")
def second = new BigDecimal("100")


println "result is " + first/second   // will print: result is 0.33333333333333
于 2010-03-18T01:08:35.713 回答
5

很确定你可以把它变成一个三行的例子。:)

如果您想要精确的精度,请使用 BigDecimal。否则,您可以使用整数乘以 10 ^ 任何您想要的精度。

于 2008-11-27T01:57:20.970 回答
5

正如其他人所指出的,并非所有十进制值都可以表示为二进制,因为十进制基于 10 的幂,而二进制基于 2 的幂。

如果精度很重要,请使用 BigDecimal,但如果您只想要友好的输出:

System.out.printf("%.2f\n", total);

会给你:

11.40
于 2008-11-27T03:52:24.920 回答
5

你不能,因为 7.3 没有二进制的有限表示。你能得到的最接近的是 2054767329987789/2**48 = 7.3+1/1407374883553280。

查看http://docs.python.org/tutorial/floatingpoint.html以获得进一步的解释。(它在 Python 网站上,但 Java 和 C++ 有相同的“问题”。)

解决方案取决于您的问题到底是什么:

  • 如果您只是不喜欢看到所有这些噪音数字,那么请修复您的字符串格式。不要显示超过 15 个有效数字(或 7 个浮点数)。
  • 如果你的数字的不精确性破坏了“if”语句之类的东西,那么你应该写 if (abs(x - 7.3) < TOLERANCE) 而不是 if (x == 7.3)。
  • 如果您正在使用金钱,那么您可能真正想要的是十进制固定点。存储整数美分或货币的最小单位。
  • (非常不可能)如果您需要超过 53 个有效位(15-16 个有效数字)的精度,则使用高精度浮点类型,例如 BigDecimal。
于 2010-03-18T00:30:23.337 回答
5

您遇到了 double 类型的精度限制。

Java.Math 有一些任意精度的算术工具。

于 2010-03-18T00:35:13.283 回答
4

使用 java.math.BigDecimal

双精度数在内部是二进制分数,因此它们有时不能将小数部分表示为精确的小数。

于 2010-03-17T23:15:40.087 回答
4
private void getRound() {
    // this is very simple and interesting 
    double a = 5, b = 3, c;
    c = a / b;
    System.out.println(" round  val is " + c);

    //  round  val is  :  1.6666666666666667
    // if you want to only two precision point with double we 
            //  can use formate option in String 
           // which takes 2 parameters one is formte specifier which 
           // shows dicimal places another double value 
    String s = String.format("%.2f", c);
    double val = Double.parseDouble(s);
    System.out.println(" val is :" + val);
    // now out put will be : val is :1.67
}
于 2011-12-07T13:41:19.717 回答
3
        /*
        0.8                     1.2
        0.7                     1.3
        0.7000000000000002      2.3
        0.7999999999999998      4.2
        */
        double adjust = fToInt + 1.0 - orgV;
        
        // The following two lines works for me. 
        String s = String.format("%.2f", adjust);
        double val = Double.parseDouble(s);

        System.out.println(val); // output: 0.8, 0.7, 0.7, 0.8
于 2020-09-18T10:25:31.107 回答
2

将所有内容乘以 100 并将其存储在 long 中。

于 2008-11-27T01:56:29.223 回答
2

计算机以二进制形式存储数字,实际上不能准确地表示诸如 33.333333333 或 100.0 之类的数字。这是使用双打的棘手问题之一。在将答案显示给用户之前,您将不得不对其进行四舍五入。幸运的是,在大多数应用程序中,您不需要那么多小数位。

于 2010-03-18T00:38:54.410 回答
2

浮点数与实数的不同之处在于,对于任何给定的浮点数,都有下一个更高的浮点数。与整数相同。1 和 2 之间没有整数。

无法将 1/3 表示为浮点数。它下面有一个浮子,上面有一个浮子,它们之间有一定的距离。1/3 在那个空间里。

Apfloat for Java 声称可以处理任意精度的浮点数,但我从未使用过它。大概值得一看。 http://www.apfloat.org/apfloat_java/

在Java 浮点高精度库之前,这里问了一个类似的问题

于 2010-03-18T00:40:47.543 回答
2

简短回答:始终使用 BigDecimal 并确保使用带有String参数的构造函数,而不是双精度参数。

回到您的示例,以下代码将根据您的意愿打印 11.4。

public class doublePrecision {
    public static void main(String[] args) {
      BigDecimal total = new BigDecimal("0");
      total = total.add(new BigDecimal("5.6"));
      total = total.add(new BigDecimal("5.8"));
      System.out.println(total);
    }
}
于 2019-09-23T15:49:03.810 回答
1

双精度数是 Java 源代码中十进制数的近似值。您会看到双精度(二进制编码值)和源(十进制编码)之间不匹配的后果。

Java 产生最接近的二进制近似值。您可以使用 java.text.DecimalFormat 来显示更好看的十进制值。

于 2008-11-27T02:22:49.407 回答
1

使用 BigDecimal。它甚至可以让您指定舍入规则(如 ROUND_HALF_EVEN,如果两者的距离相同,它将通过舍入到偶数邻居来最小化统计误差;即 1.5 和 2.5 都舍入到 2)。

于 2008-11-27T02:36:26.227 回答
1

为什么不使用 Math 类的 round() 方法?

// The number of 0s determines how many digits you want after the floating point
// (here one digit)
total = (double)Math.round(total * 10) / 10;
System.out.println(total); // prints 11.4
于 2015-11-14T16:05:34.067 回答
0

查看 BigDecimal,它可以处理处理浮点运算的问题。

新调用如下所示:

term[number].coefficient.add(co);

使用 setScale() 设置要使用的小数位数精度。

于 2010-03-17T23:14:14.300 回答
0

如果您别无选择,只能使用双精度值,可以使用以下代码。

public static double sumDouble(double value1, double value2) {
    double sum = 0.0;
    String value1Str = Double.toString(value1);
    int decimalIndex = value1Str.indexOf(".");
    int value1Precision = 0;
    if (decimalIndex != -1) {
        value1Precision = (value1Str.length() - 1) - decimalIndex;
    }

    String value2Str = Double.toString(value2);
    decimalIndex = value2Str.indexOf(".");
    int value2Precision = 0;
    if (decimalIndex != -1) {
        value2Precision = (value2Str.length() - 1) - decimalIndex;
    }

    int maxPrecision = value1Precision > value2Precision ? value1Precision : value2Precision;
    sum = value1 + value2;
    String s = String.format("%." + maxPrecision + "f", sum);
    sum = Double.parseDouble(s);
    return sum;
}
于 2018-05-07T10:59:58.363 回答
0

您可以执行以下操作!

System.out.println(String.format("%.12f", total));

如果您在此处更改十进制值%.12f

于 2021-10-17T19:45:45.847 回答
-2

不要使用 BigDecimal 浪费您的精力。在 99.99999% 的情况下,您不需要它。java double类型当然是近似的,但在几乎所有情况下,它都足够精确。请注意,您在第 14 位有效数字处有错误。这真的可以忽略不计!

要获得不错的输出,请使用:

System.out.printf("%.2f\n", total);
于 2010-05-21T12:16:14.860 回答