概述
文章目录
- 0.59f * 100 和 0.59 * 100
- 二进制基础知识:
- 二进制数和10进制数字的相互转换
- 计算机对浮点数的存储
- 避免精度丢失:
- 两个数相除得到double类型
0.59f * 100 和 0.59 * 100
上面两个运算公式的运算结果如下:
int a1 = (int) (0.59f * 100);
System.out.println(a1); // 输出:58
int a2 = (int) (0.59 * 100);
System.out.println(a2); // 输出:59
java中小数不带标识默认是double类型
好家伙,这是为啥呢,第一个居然是58,由于不明白上面的运算问题,一到简单题我愣是没搞出来????。
上面的运算中使用了float和double来进行小数运算,但是还记得有句话是这样说的,使用float和double会有精度丢失,网上搜了搜才知道,上面的问题就正好遇到了float运算精度丢失的问题。而恰好double没有丢失精度。
为什么这样计算会丢失精度,先一起复习下计算机中二进制和十进制的转换:
二进制基础知识:
二进制数和10进制数字的相互转换
二进制数转为10进制数:
10进制转为2进制数:
当然上面的0.625只是刚好可以表示为二进制,并没有精度损失的问题,
计算机对浮点数的存储
IEEE754是一个最广为使用的浮点数运算标准,
图片出处:https://blog.csdn.net/user2025/article/details/107746452
float和double表示的数值的精度范围:
参考:https://baike.baidu.com/item/IEEE%20754/3869922?fr=aladdin
而对于我们上面计算公式中的0.59转为二进制表示为:
十进制小数 | 基数 | 积 | 取整 |
---|---|---|---|
0.59 | 2 | 1.18 | 1 |
0.18 | 2 | 0.36 | 0 |
0.36 | 2 | 0.72 | 0 |
0.72 | 2 | 1.44 | 1 |
0.44 | 2 | 0.88 | 0 |
0.88 | 2 | 1.76 | 1 |
… |
再往下按照二进制转10进制的规则,可以一直计算得到(自己借助这个平台计算的)
0.59=0.10010111000010100011110101110000101000111101011100001
因为floatM只有23位,外加开头默认的1,一共可以存储24位,即计算的时候实际使用的是:
0.10010111 00001010 00111101 参与运算,这样的话结果就为58了,
上面的小数部分刚好为53位,所以用double类型计算没有出现错误。
避免精度丢失:
使用float和double运算时会造成结果的不准确,因此浮点数运算的时候,我们可以使用java提供的BigDecimal,使用BigDecimal对小数进行运算的时候,会先将数字扩大N倍,同时保存精度,转为整数进行相应的运算,最后再转为小数。
题目:https://leetcode.cn/problems/percentage-of-letter-in-string/
当然这道题使用double也可以
public int percentageLetter(String s, char letter) {
int frequent = 0;
char[] chars = s.toCharArray();
for (int i = 0; i < s.length(); i++) {
if (chars[i] == letter) {
frequent++;
}
}
if (frequent == 0) {
return 0;
}
if (frequent == s.length()) {
return 1;
}
BigDecimal bigDecimal1 = new BigDecimal(String.valueOf(frequent));
BigDecimal bigDecimal2 = new BigDecimal(String.valueOf(s.length()));
return (int) (bigDecimal1.divide(bigDecimal2, 4, RoundingMode.FLOOR).multiply(new BigDecimal("100")).doubleValue());
}
两个数相除得到double类型
下面的代码:
double b = 499999999d / 500000000;
System.out.println("b is:" + b);
double c = 499999998d / 499999999;
System.out.println("c is:" + c);
在我的电脑上输出是:
b is:0.999999998
c is:0.999999998
这两个小数我的电脑计算出来的结果是相等,但是leetcode提交过不了,我应该算错了,
如果 y1/x1=y2/x2,那么转换为乘法,y1x2=y2x1,
转换为下面的公式:
long d = 499999999L * 499999999;
long e = 499999998L * 500000000;
System.out.println(d);
System.out.println(e);
输出:
249999999000000001
249999999000000000
这样计算又是不相等的,那到底是哪个错了?再使用BigDecimal进行除法运算计算下,看下准确的结果,根据上面的公式double最多保留15.95位十进制数,这里使用BigDecimal运算的时候我保留20位的小数
BigDecimal dec1 = new BigDecimal(String.valueOf(499999998));
BigDecimal dec2 = new BigDecimal(String.valueOf(499999999));
BigDecimal dec3 = new BigDecimal(String.valueOf(499999999));
BigDecimal dex4 = new BigDecimal(String.valueOf(500000000));
System.out.println(dec1.divide(dec2, 20, RoundingMode.FLOOR).toString());
System.out.println(dec3.divide(dex4, 20, RoundingMode.FLOOR).toString());
输出:
0.99999999799999999599
0.99999999800000000000
可以看到,499999998d / 499999999 计算的结果对比用BigDecimal计算的结果,结果小数部被舍弃了一些,所以导致最终的结果错误,因此计算double除法的时候要小心,进行下公式转换,或使用BigDecimal。
参考:
https://cloud.tencent.com/developer/article/1470383
https://blog.csdn.net/user2025/article/details/107746452
题目:
https://leetcode.cn/contest/weekly-contest-294/
最后
以上就是甜美飞鸟为你收集整理的java运算中的精度问题的全部内容,希望文章能够帮你解决java运算中的精度问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复