在Java中保持双精度
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?
正如其他人所提到的,如果你想精确地expression11.4,你可能会想使用BigDecimal
类。
现在,解释一下为什么会发生这种情况:
Java中的float
和double
基本types是浮点数 ,其中数字是以分数和指数的二进制表示forms存储的。
更具体地说,诸如double
types的双精度浮点值是64位值,其中:
- 1位表示符号(正或负)。
- 指数为11位。
- 有效数字为52位(小数部分为二进制)。
这些部分相结合产生一个值的double
表示。
(来源: 维基百科:双精度 )
有关如何在Java中处理浮点值的详细说明,请参见Java语言规范的第4.2.3节:浮点types,格式和值 。
byte
, char
, int
, long
types是定点数字,它们是数字的精确表示。 与定点数不同的是,浮点数有时候(大多数情况下是安全的)不能返回数字的精确表示。 这就是为什么你以5.6 + 5.8
结果11.399999999999
的原因。
当需要一个精确的值,比如1.5或150.1005时,你需要使用一个定点types,这个定点types可以精确地表示数字。
正如已经多次提到的那样,Java有一个BigDecimal
类,它可以处理非常大的数字和很小的数字。
从BigDecimal
类的Java API参考:
不可变的任意精度带符号的十进制数字。 BigDecimal由一个任意的精度整数非标定值和一个32位整数标度组成。 如果为零或正数,则标度是小数点右侧的位数。 如果是负数,则该数字的非缩放值乘以十的倍数的否定的比例的权力。 由BigDecimal表示的数字的值因此是(unscaledValue×10 ^ -scale)。
关于浮点数的问题及其精度,Stack Overflow有很多问题。 以下是可能感兴趣的相关问题清单:
- 为什么我会看到一个双variables初始化为21.4,如21.399999618530273?
- 如何在C ++中打印真正的大数字
- 如何存储浮点数? 什么时候重要?
- 会计申请美元金额使用浮点数还是小数点?
如果你真的想深入了解浮点数的细节,看看每个计算机科学家应该知道的浮点运算 。
当你input一个双33.33333333333333
数时,例如33.33333333333333
,你得到的值实际上是最接近的可表示的双精度值,这正好是:
33.3333333333333285963817615993320941925048828125
除以100得出:
0.333333333333333285963817615993320941925048828125
这也不能表示为一个双精度数字,所以再次将其四舍五入到最接近的可表示的值,这正是:
0.3333333333333332593184650249895639717578887939453125
当你输出这个值时,它又被舍入到十七个十进制数字,给出:
0.33333333333333326
如果您只是想将值作为分数进行处理,您可以创build一个分数类,它包含分子和分母字段。
编写加法,减法,乘法和除法以及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); } } }
如果你使用有限精度的十进制算术,并且想要处理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.Formatter
或String.format
。 在格式string中指定一个小于double的完整精度的精度。 对于10位有效数字,比如11.399999999999是11.4,所以在二进制结果非常接近只需要小数点后几位的情况下,结果将几乎一样准确,更易读。
指定的精确度取决于你对数字做了多lessmath – 一般来说,你做的越多,积累的误差越多,但是一些algorithm比其他algorithm积累速度快得多(他们被称为“不稳定”反对舍入误差的“稳定”)。 如果你正在做的只是添加一些值,那么我猜测只要放下一个精度的小数点即可。 实验。
如果你真的需要精确的math,你可能要考虑使用Java的java.math.BigDecimal类。 这里有一篇关于BigDecimal的案例来自Oracle / Sun。 虽然你不能像某人所说的那样代表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
正如其他人所指出的,并非所有的十进制值都可以表示为二进制,因为十进制是基于10的幂,而二进制是基于2的幂。
如果精度很重要,请使用BigDecimal,但是如果您只需要友好输出:
System.out.printf("%.2f\n", total);
会给你:
11.40
很确定,你可以把它变成一个三线的例子。 🙂
如果你想要精确的精度,使用BigDecimal。 否则,你可以使用整数乘以10 ^无论你想要的精度。
你正在运行对doubletypes的精度限制。
Java.Math有一些任意精度的算术工具。
你不能,因为7.3在二进制中没有有限的表示。 你可以得到最接近的2054767329987789/2 ** 48 = 7.3 + 1/1407374883553280。
看看http://docs.python.org/tutorial/floatingpoint.html进一步的解释。; (这是在Python网站上,但Java和C ++有相同的“问题”。)
解决scheme取决于你的问题是什么:
- 如果你只是不喜欢看到所有的噪音数字,然后修复你的string格式。 不要显示超过15个有效数字(或7个浮点数)。
- 如果你的数字的不精确性违反了“if”语句,那么你应该写if(abs(x – 7.3)<TOLERANCE)而不是if(x == 7.3)。
- 如果你正在用钱工作,那么你可能真正想要的是十进制定点。 存储整数美分或任何最小单位的货币。
- (非常不精确)如果你需要超过53个有效位(15-16位有效数字)的精度,那么使用高精度的浮点types,比如BigDecimal。
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 }
使用java.math.BigDecimal
双数是内部的二进制分数,所以他们有时不能将十进制小数表示为精确的小数。
把所有的东西都乘以100,然后存放在一个美分的长度内。
计算机以二进制forms存储数字,实际上不能完全表示数字,例如33.333333333或100.0。 这是使用双打棘手的事情之一。 在将其展示给用户之前,您只能围绕该答案。 幸运的是,在大多数应用程序中,无论如何你都不需要那么多的小数位。
浮点数与实数的不同之处在于,对于任何给定的浮点数,都有一个更高的浮点数。 与整数相同。 没有1和2之间的整数。
没有办法将1/3作为浮点数来表示。 它下面有一个浮标,上面有一个浮标,它们之间有一定的距离。 三分之一在这个空间。
Apfloat for Java声称可以使用任意精度的浮点数,但是我从来没有使用它。 可能值得一看。 http://www.apfloat.org/apfloat_java/
在Java浮点高精度库之前,在这里提出了类似的问题
加倍是Java源代码中的十进制数的近似值 。 你会看到double(它是一个二进制编码的值)和你的源(十进制编码)之间的不匹配的结果。
Java产生最接近的二进制近似值。 您可以使用java.text.DecimalFormat来显示更好看的小数值。
使用BigDecimal。 它甚至可以让你指定舍入规则(如ROUND_HALF_EVEN,如果两者距离相同,即将1.5和2.5舍入为2,则舍入到偶数邻居可以最小化统计错误)。
检查BigDecimal,它处理像这样的浮点运算问题。
新的呼叫将如下所示:
term[number].coefficient.add(co);
使用setScale()设置要使用的小数位精度的数量。
为什么不使用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
不要用BigDecimal浪费你的时间。 在99.99999%的情况下,你不需要它。 java doubletypes是近似的,但是在几乎所有的情况下,都是非常精确的。 请注意,你的第14位有一个错误。 这真是微不足道!
为了得到不错的输出使用:
System.out.printf("%.2f\n", total);