Answers:
正如其他人所提到的BigDecimal
,如果您希望精确表示为11.4,则可能要使用该类。
现在,对造成这种情况的原因进行一些解释:
Java中的float
和double
类型是浮点数,其中的数字以小数和指数的二进制表示形式存储。
更具体地说,诸如double
类型的双精度浮点值是64位值,其中:
这些部分被组合以产生double
值的表示。
(来源:维基百科:双精度)
有关Java中如何处理浮点值的详细说明,请参见第4.2.3节:浮点类型,格式和值《 Java语言规范》。
的byte
,char
,int
,long
类型的定点数字,这是数字的精确representions。与定点数不同,浮点数有时(可以安全地假定为“大部分时间”)不能返回数字的精确表示形式。这就是为什么你最终的原因11.399999999999
为的结果5.6 + 5.8
。
当需要一个精确的值(例如1.5或150.1005)时,您将需要使用一种定点类型,该类型将能够精确表示该数字。
正如已经多次提到的,Java有一个BigDecimal
可以处理非常大和非常小的数字的类。
从BigDecimal
该类的Java API参考中:
不变的,任意精度的带符号十进制数字。BigDecimal由任意精度的整数无标度值和32位整数标度组成。如果为零或正数,则小数位数是小数点右边的位数。如果为负,则该数字的未标度值乘以十,即标度取反的幂。因此,由BigDecimal表示的数字的值为(unscaledValue×10 ^ -scale)。
关于浮点数及其精度的堆栈溢出问题很多。这是您可能感兴趣的相关问题列表:
如果您真的想深入了解浮点数的细节,请查看每位计算机科学家应该了解的浮点算术知识。
BigDecimal
比double
这种情况要慢得多,但因为double具有15个小数位的精度,所以您不需要四舍五入。
当输入一个双数时,例如,33.33333333333333
您得到的值实际上是最接近的可表示双精度值,即:
33.3333333333333285963817615993320941925048828125
除以100可得出:
0.333333333333333285963817615993320941925048828125
也不能将其表示为双精度数,因此再次将其舍入为最接近的可表示值,即:
0.3333333333333332593184650249895639717578887939453125
当您打印出该值时,它将再次舍入到17位十进制数字,从而得到:
0.33333333333333326
如果只想将值作为分数处理,则可以创建一个包含分子和分母字段的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);
}
}
}
numerator
并且denominator
应该是int
S' 为什么要浮点精度?
如果使用有限精度的十进制算术,并且要处理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
。在格式字符串中,指定小于双精度全精度的精度。对于10个有效数字,例如11.399999999999为11.4,因此在二进制结果非常接近仅需要小数位的值的情况下,结果将几乎一样准确且更易于理解。
指定的精度在某种程度上取决于您对数字进行的数学处理-一般来说,您做的越多,累积的错误就越多,但是某些算法的累积速度比其他算法快(它们被称为“不稳定”算法,因为在舍入错误方面与“稳定”相对)。如果您要做的只是添加一些值,那么我猜想只降低小数点后一位的精度就可以解决问题。实验。
如果确实需要精确数学,则可能需要研究使用java的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
您遇到了double类型的精度限制。
Java.Math具有一些任意精度的算术功能。
您不能,因为7.3没有二进制形式的有限表示形式。您可以获得的最接近的数字是2054767329987789/2 ** 48 = 7.3 + 1/1407374883553280。
查看http://docs.python.org/tutorial/floatingpoint.html以获得进一步的说明。(它在Python网站上,但是Java和C ++具有相同的“问题”。)
解决方案取决于您的问题到底是什么:
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,并将其存储在很长的美分中。
浮点数与实数的不同之处在于,对于任何给定的浮点数,都有下一个更高的浮点数。与整数相同。1到2之间没有整数。
无法将1/3表示为浮点数。在它下面有一个浮子,在它上面有一个浮子,它们之间有一定的距离。1/3在那个空间中。
Apfloat for Java声称可以使用任意精度的浮点数,但是我从未使用过。大概值得一看。 http://www.apfloat.org/apfloat_java/
在Java浮点高精度库之前,这里曾问过类似的问题
使用BigDecimal。它甚至允许您指定舍入规则(例如ROUND_HALF_EVEN,如果两者的距离相同,则通过对偶数邻居进行舍入来最小化统计误差;即1.5和2.5都舍入为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);
}
}
如果除了使用双精度值之外别无选择,可以使用以下代码。
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;
}