在 Java 中使用 double 保持精度

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?

247280 次浏览

把所有东西都乘以100然后存起来只要几分钱。

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

如果需要精确的精度,请使用 BigDecimal。否则,可以使用 int 乘以10 ^ 任意精度。

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

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

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

请注意,如果使用有限精度的十进制算法,并且希望处理1/3:0.333333333 * 3是0.9999999,而不是1.0000000,那么也会出现同样的问题。

不幸的是,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.3999999999999是11.4,所以在二进制结果非常接近只需要几个小数位的值的情况下,结果几乎同样准确,而且更易于人类阅读。

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

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

现在,解释一下为什么会这样:

Java 中的 floatdouble基元类型是 浮点数数字,其中数字以分数和指数的二进制表示形式存储。

更具体地说,双精度浮点值(如 double类型)是一个64位值,其中:

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

这些部分组合在一起生成一个值的 double表示形式。

(资料来源: 维基百科: 双精度)

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

bytecharintlong类型是 定点数字,它们是数字的精确表示。与固定点数不同,浮点数有时(可以假设“大部分时间”)无法返回数字的精确表示。这就是为什么你结束与 11.399999999999作为 5.6 + 5.8的结果。

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

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

来自 BigDecimal类的 JavaAPI 参考:

永恒不变, 任意精度有符号小数 BigDecimal 由 任意精度整数不可缩放 值和32位整数比例。如果 0或正值,刻度是 右侧的位数 小数点。如果为负,则 数字的未缩放值为 乘以10的功率 刻度的否定。刻度的值 表示的数字 因此 BigDecimal 是(unscaledValue × 10 ^-比例)。

关于堆栈溢出问题,在浮点数及其精度方面存在许多问题。下面是一些可能会引起兴趣的相关问题:

如果你真的想了解浮点数的细节,看看 关于浮点算法,每个计算机科学家都应该知道什么

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

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

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

会给你:

11.40

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

新的电话会是这样的:

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

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

使用 java.math.BigDecimal

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

你不能,因为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。

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

爪哇数学有一些高精度计算。

计算机以二进制形式存储数字,实际上不能准确地表示33.33333333或100.0这样的数字。这是使用双精度浮点数的技巧之一。在向用户显示答案之前,您必须对答案进行四舍五入。幸运的是,在大多数应用程序中,无论如何都不需要那么多小数位。

浮点数不同于实数,因为对于任何给定的浮点数,都有一个更高的浮点数。和整数一样。1到2之间没有整数。

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

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

以前也有人问过类似的问题 Java 浮点型高精度库

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

33.3333333333333285963817615993320941925048828125

除以100得到:

0.333333333333333285963817615993320941925048828125

它也不能表示为双精度数,所以它也是四舍五入到最接近的可表示值,正好是:

0.3333333333333332593184650249895639717578887939453125

当你打印出这个值时,它会四舍五入 又一次到17位小数,给出:

0.33333333333333326

您可能需要研究一下使用 java 的 java.data。如果您确实需要精确数学,可以使用 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

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


}

不要浪费时间使用 BigDecimal。在99.99999% 的情况下你不需要它。Java 双倍类型当然是近似的,但在几乎所有情况下,它都是足够精确的。请注意,在第14位有效数字处有一个错误。这真是微不足道!

为了获得良好的输出使用:

System.out.printf("%.2f\n", total);
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
}

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

如果除了使用双精度值外别无选择,可以使用下面的代码。

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

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

回到您的示例,下面的代码将根据您的需要打印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);
}
}
        /*
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

你可以做到以下几点!

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

如果你改变这里的十进制值 % .12 f

到目前为止,我理解它作为主要目标,以获得正确的 double从错误的 double

寻找我的解决方案如何得到正确的值从“近似”错误的值-如果它是真正的浮点数它舍去最后一个数字-从所有数字计数-计数前点和尝试保持最大可能的数字后点-希望它是足够的精度为大多数情况下:

public static double roundError(double value) {
BigDecimal valueBigDecimal = new BigDecimal(Double.toString(value));
String valueString = valueBigDecimal.toPlainString();
if (!valueString.contains(".")) return value;
String[] valueArray = valueString.split("[.]");
int places = 16;
places -= valueArray[0].length();
if ("56789".contains("" + valueArray[0].charAt(valueArray[0].length() - 1))) places--;
//System.out.println("Rounding " + value + "(" + valueString + ") to " + places + " places");
return valueBigDecimal.setScale(places, RoundingMode.HALF_UP).doubleValue();
}

我知道这是长代码,肯定不是最好的,也许有人可以修复它更优雅。不管怎么说,它确实起作用了,看看这些例子:

roundError(5.6+5.8) = 11.399999999999999 = 11.4
roundError(0.4-0.3) = 0.10000000000000003 = 0.1
roundError(37235.137567000005) = 37235.137567
roundError(1/3) 0.3333333333333333 = 0.333333333333333
roundError(3723513756.7000005) = 3.7235137567E9 (3723513756.7)
roundError(3723513756123.7000005) = 3.7235137561237E12 (3723513756123.7)
roundError(372351375612.7000005) = 3.723513756127E11 (372351375612.7)
roundError(1.7976931348623157) = 1.797693134862316