在Java中保留double的精度

时间:2008-11-27 01:54:04

标签: java floating-point double precision

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?

22 个答案:

答案 0 :(得分:130)

正如其他人所提到的,如果你想要一个11.4的精确表示,你可能想要使用BigDecimal类。

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

Java中的floatdouble基元类型是floating point个数字,其中数字存储为分数和指数的二进制表示。

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

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

组合这些部分以产生值的double表示。

(资料来源:Wikipedia: Double precision

有关如何在Java中处理浮点值的详细说明,请参阅Java语言规范的Section 4.2.3: Floating-Point Types, Formats, and Values

bytecharintlong类型是fixed-point个数字,它们是数字的精确代表。与定点数不同,浮点数有时会(可以安全地假设“大部分时间”)无法返回数字的精确表示。这就是11.399999999999

结果导致5.6 + 5.8结束的原因。

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

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

来自BigDecimal类的Java API参考:

  

不可变的,   任意精度的有符号小数   数字。 BigDecimal由一个   任意精度整数未缩放   值和32位整数标度。如果   零或正,规模是   的右边的位数   小数点。如果是否定的话   未缩放的数字值是   乘以十来得到的力量   否定规模。的价值   由...表示的数字   因此BigDecimal(unscaledValue   ×10 ^ -scale)。

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

如果您真的想了解浮点数的细节,请查看What Every Computer Scientist Should Know About Floating-Point Arithmetic

答案 1 :(得分:99)

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

33.3333333333333285963817615993320941925048828125

除以100得出:

0.333333333333333285963817615993320941925048828125

也不能表示为双精度数,因此它再次舍入到最接近的可表示值,这恰好是:

0.3333333333333332593184650249895639717578887939453125

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

0.33333333333333326

答案 2 :(得分:23)

如果您只想将值作为分数处理,可以创建一个包含分子和分母字段的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);
    }
}

}

答案 3 :(得分:15)

如果使用有限精度的十进制算法,并且想要处理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。在格式字符串中,指定精度小于double的完整精度。对于10个有效数字,例如11.399999999999是11.4,因此在二进制结果非常接近只需要几个小数位的值的情况下,结果几乎同样准确且更易于阅读。

要指定的精度取决于你对数字做了多少数学运算 - 一般来说,你做的越多,误差就越多,但是有些算法比其他算法累积得更快(它们被称为“相对于舍入误差,“不稳定”而不是“稳定”。如果您所做的只是添加一些值,那么我猜测只丢一个小数位的精度就可以解决问题。实验

答案 4 :(得分:9)

如果你真的需要精确数学,你可能想要研究使用java的java.math.BigDecimal类。以下是Oracle / Sun在the case for 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

答案 5 :(得分:5)

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

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

答案 6 :(得分:5)

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

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

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

会给你:

11.40

答案 7 :(得分: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。

答案 8 :(得分:5)

你正在遇到类型为double的精度限制。

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

答案 9 :(得分: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
}

答案 10 :(得分:3)

使用java.math.BigDecimal

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

答案 11 :(得分:2)

计算机以二进制形式存储数字,实际上并不能完全代表33.333333333或100.0等数字。这是使用双打的棘手问题之一。在向用户显示答案之前,您必须将答案四舍五入。幸运的是,在大多数应用程序中,无论如何都不需要那么多小数位。

答案 12 :(得分:2)

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

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

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

此前有人问过类似的问题 Java floating point high precision library

答案 13 :(得分:2)

将所有内容乘以100并将其存储在一个长的分数内。

答案 14 :(得分:1)

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

答案 15 :(得分:1)

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

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

答案 16 :(得分:0)

检查BigDecimal,它处理像这样处理浮点运算的问题。

新电话会是这样的:

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

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

答案 17 :(得分:0)

为什么不使用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

答案 18 :(得分:0)

如果除了使用double值之外别无选择,可以使用以下代码。

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;
}

答案 19 :(得分:0)

简短答案:请始终使用BigDecimal,并确保您使用的构造函数带有字符串参数,而不是双精度整数。

回到您的示例,根据您的需要,以下代码将显示11.4。

public class doublePrecision {
    public static void main(String[] args) {
      double total = new BigDecimal("0");
      total = total.add(new BigDecimal("5.6");
      total = total.add(new BigDecimal("5.8");
      System.out.println(total);
    }
}

答案 20 :(得分:0)

        /*
        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

答案 21 :(得分:-1)

不要使用BigDecimal浪费你的efford。在99.99999%的情况下,您不需要它。 java double 类型是近似的,但在几乎所有情况下,它都足够精确。请注意,您的第14位有错误。 这实际上可以忽略不计!

要获得不错的输出:

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