float f0 = 0.021f;
float f1 = 0.022f;
out.println(f0);
out.println(f1);
out.println(f0 + f1);
结果:
0.021
0.022
0.042999998
为什么不是0.043?

Java Float类型 减法运算时精度丢失问题​www.blogjava.net

package test1;

public class Test2 {

/**
* @param args
*/
public static void main(String[] args) {
Float xx = 2.0f;
Float yy = 1.8f;
Float tt = xx - yy;
System.out.println("tttttt-----" + tt);
}

}

果然输出结果是: tttttt-----0.20000005

再测试了几个float类型的减法,除了*.0这样的相减没有异议之外,都存在这个问题,就是说float在相减的时候精度丢失了。后来在网上找到一段解决这个问题的办法,记在这里:

package test1;

import java.math.BigDecimal;

public class Test2 {

/**
* @param args
*/
public static void main(String[] args) {
Float xx = 2.2f;
Float yy = 2.0f;
Float tt = xx - yy;
BigDecimal b1 = new BigDecimal(Float.toString(xx));
BigDecimal b2 = new BigDecimal(Float.toString(yy));
float ss = b1.subtract(b2).floatValue();
System.out.println("ssss----" + ss);
System.out.println("tttttt-----" + tt);
}
}
输出为:

ssss----0.2
tttttt-----0.20000005

这样一对比,差异就很明显了。

解决了问题,再找了一下为什么会产生这种差异:

网上有篇文章写得很详细,标题为《剖析float型的内存存储和精度丢失问题》,全文内容如下:

问题提出:12.0f-11.9f=0.10000038,"减不尽"为什么?

现在我们就详细剖析一下浮点型运算为什么会造成精度丢失?

1、小数的二进制表示问题

首先我们要搞清楚下面两个问题:

(1) 十进制整数如何转化为二进制数

算法很简单。举个例子,11表示成二进制数:

11/2=5 余 1

5/2=2 余 1

2/2=1 余 0

1/2=0 余 1

0结束 11二进制表示为(从下往上):1011

这里提一点:只要遇到除以后的结果为0了就结束了,大家想一想,所有的整数除以2是不是一定能够最终得到0。换句话说,所有的整数转变为二进制数的算法会不会无限循环下去呢?绝对不会,整数永远可以用二进制精确表示 ,但小数就不一定了。

(2) 十进制小数如何转化为二进制数

算法是乘以2直到没有了小数为止。举个例子,0.9表示成二进制数

0.9*2=1.8 取整数部分 1

0.8(1.8的小数部分)*2=1.6 取整数部分 1

0.6*2=1.2 取整数部分 1

0.2*2=0.4 取整数部分 0

0.4*2=0.8 取整数部分 0

0.8*2=1.6 取整数部分 1

0.6*2=1.2 取整数部分 0

......... 0.9二进制表示为(从上往下): 1100100100100......

注意:上面的计算过程循环了,也就是说*2永远不可能消灭小数部分,这样算法将无限下去。很显然,小数的二进制表示有时是不可能精确的 。其实道理很简单,十进制系统中能不能准确表示出1/3呢?同样二进制系统也无法准确表示1/10。这也就解释了为什么浮点型减法出现了"减不尽"的精度丢失问题。

2、 float型在内存中的存储

众所周知、 Java 的float型在内存中占4个字节。float的32个二进制位结构如下

float内存存储结构

4bytes 31 30 29----23 22----0

表示 实数符号位 指数符号位 指数位 有效数位

其中符号位1表示正,0表示负。有效位数位24位,其中一位是实数符号位。

将一个float型转化为内存存储格式的步骤为:

(1)先将这个实数的绝对值化为二进制格式,注意实数的整数部分和小数部分的二进制方法在上面已经探讨过了。
(2)将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边。
(3)从小数点右边第一位开始数出二十三位数字放入第22到第0位。
(4)如果实数是正的,则在第31位放入“0”,否则放入“1”。
(5)如果n 是左移得到的,说明指数是正的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”。
(6)如果n是左移得到的,则将n减去1后化为二进制,并在左边加“0”补足七位,放入第29到第23位。如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位。

举例说明: 11.9的内存存储格式

(1) 将11.9化为二进制后大约是" 1011. 1110011001100110011001100..."。

(2) 将小数点左移三位到第一个有效位右侧: "1. 011 11100110011001100110 "。 保证有效位数24位,右侧多余的截取(误差在这里产生了 )。

(3) 这已经有了二十四位有效数字,将最左边一位“1”去掉,得到“ 011 11100110011001100110 ”共23bit。将它放入float存储结构的第22到第0位。

(4) 因为11.9是正数,因此在第31位实数符号位放入“0”。

(5) 由于我们把小数点左移,因此在第30位指数符号位放入“1”。

(6) 因为我们是把小数点左移3位,因此将3减去1得2,化为二进制,并补足7位得到0000010,放入第29到第23位。

最后表示11.9为: 0 1 0000010 011 11100110011001100110

再举一个例子:0.2356的内存存储格式
(1)将0.2356化为二进制后大约是0.00111100010100000100100000。
(2)将小数点右移三位得到1.11100010100000100100000。
(3)从小数点右边数出二十三位有效数字,即11100010100000100100000放
入第22到第0位。
(4)由于0.2356是正的,所以在第31位放入“0”。
(5)由于我们把小数点右移了,所以在第30位放入“0”。
(6)因为小数点被右移了3位,所以将3化为二进制,在左边补“0”补足七
位,得到0000011,各位取反,得到1111100,放入第29到第23位。

最后表示0.2356为:0 0 1111100 11100010100000100100000

将一个内存存储的float二进制格式转化为十进制的步骤:
(1)将第22位到第0位的二进制数写出来,在最左边补一位“1”,得到二十四位有效数字。将小数点点在最左边那个“1”的右边。
(2)取出第29到第23位所表示的值n。当30位是“0”时将n各位求反。当30位是“1”时将n增1。
(3)将小数点左移n位(当30位是“0”时)或右移n位(当30位是“1”时),得到一个二进制表示的实数。
(4)将这个二进制实数化为十进制,并根据第31位是“0”还是“1”加上正号或负号即可。

3、浮点型的减法运算

浮点加减运算过程比定点运算过程复杂。完成浮点加减运算的操作过程大体分为四步:
(1) 0操作数的检查;

如果判断两个需要加减的浮点数有一个为0,即可得知运算结果而没有必要再进行有序的一些列操作。

(2) 比较阶码(指数位)大小并完成对阶;

两浮点数进行加减,首先要看两数的 指数位 是否相同,即小数点位置是否对齐。若两数 指数位 相同,表示小数点是对齐的,就可以进行尾数的加减运算。反之,若两数阶码不同,表示小数点位置没有对齐,此时必须使两数的阶码相同,这个过程叫做对阶

如何对 阶(假设两浮点数的指数位为 Ex 和 Ey ):

通过尾数的移位以改变 Ex 或 Ey ,使之相等。 由 于浮点表示的数多是规格化的,尾数左移会引起最高有位的丢失,造成很大误差;而尾数右移虽引起最低有效位的丢失,但造成的误差较小,因此,对阶操作规定使 尾数右移,尾数右移后使阶码作相应增加,其数值保持不变。很显然,一个增加后的阶码与另一个相等,所增加的阶码一定是小阶。因此在对阶时,总是使小阶向大阶看齐 ,即小阶的尾数向右移位 ( 相当于小数点左移 ) ,每右移一位,其阶码加 1 ,直到两数的阶码相等为止,右移的位数等于阶差 △ E 。
(3) 尾数(有效数位)进行加或减运算;

对阶完毕后就可 有效数位 求和。 不论是加法运算还是减法运算,都按加法进行操作,其方法与定点加减运算完全一样。
(4) 结果规格化并进行舍入处理。

4、 计算12.0f-11.9f

12.0f 的内存存储格式为: 0 1 0000010 10000000000000000000000

11.9f 的内存存储格式为: 0 1 0000010 011 11100110011001100110

可见两数的指数位完全相同,只要对有效数位进行减法即可。

12.0f-11.9f 结果: 0 1 0000010 00000011001100110011010

将结果还原为十进制为: 0.000 11001100110011010= 0.10000038

float 精度_float相加产生精度损失的原因是什么?相关推荐

  1. float计算丢精度_float精度损失 int 转 float 为什么有时会丢失精度

    float型运算为什么会损失精度 float占用32bit,其中24bit用于表示有效数字. float rv = 0.0; rv = 88889999;为什么调试程序的时候发现赋值后rv 编程由于字 ...

  2. MySQL工作笔记-建表时为Float型数据确定精度,查询时精度显示,多列之和查询

    目录 建表时设置精度 查询时设置精度 查询多列之和 建表时设置精度 首先是建表时为float类型设置精度: SQL代码如下: CREATE TABLE `testtable` (`id` int(11 ...

  3. vue 使用 currency.js 处理 小数的相加减 精度问题

    vue 使用 currency.js 处理 小数的相加减 精度问题 安装 currency.js npm install --save currency.js 或者 yarn add currency ...

  4. 神经网络训练精度一直为1,损失为0

    错误 最近在复现同事的项目时,发现从第一次迭代到结束训练,网络精度基本为1,损失基本为0. 原因 检查了相关文档,发现在制作标签文档时将标签相同数据写在了一起,导致前期读入数据时全是一个标签,误导了模 ...

  5. 用 float 存储金额,老板说损失从工资里扣!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 本文来源: juejin.im/post/5c08db5ff265 ...

  6. mybatis float 小数0 不显示_卧槽!用 float 存储金额,老板说损失从工资里扣!

    开发者(KaiFaX) 我们都是开发者专注于前端.后端.大数据.区块链.人工智能的知识社区 来源:https://juejin.im/post/5c08db5ff265da611e4d7417 作者: ...

  7. 读数精度与满量程精度

    精度是测量值与真值的接近程度.包含精密度,准确度和精确度三个方面.在测试仪器行业,不确定度(很多人称之为精度,因此下面就称之为精度)是仪器的一个最重要指标之一,而不确定度指标的表示是有不同的方式的. ...

  8. 在计算机领域,半精度、单精度、双精度的定义,以及多精度计算和混合精度计算的区别。

    在计算机系统的内存中,半精度是16bit,单精度是32bit,双精度是64bit. signed bit符号位,有效数字的符号位 Exponent 阶码或者叫指数,以10^Exponent表示 Sig ...

  9. float计算丢精度_Float和double丢失精度问题及解决方案

    出现这种结果的原因:float和double类型尤其不适合用于货币运算,因为要让一个float或double精确的表示0.1或者任何其他负数次方值是不可能的(十进制系统中不能准确的表示出1/3,同样二 ...

最新文章

  1. tf.data.Dataset.from_tensor_slices 的用法
  2. J2EE的13个规范总结
  3. docker 根据标签删除镜像_10 个 Docker 镜像安全最佳实践
  4. mysql5.6 1g内存_1G内存用MySQL5.6还是用MySQL5.5比较好
  5. SVN还原与SVN更新区别
  6. java 类数组_Java常用类-字符串、日期类、算法及数组工具类等
  7. Java thread.join
  8. python文献检索工具与技巧答案_短文本分析----基于python的TF-IDF特征词标签自动化提取...
  9. nginx rtmp HLS直播
  10. 操作系统 第一部分 操作系统的基本概念
  11. 本科生、研究生查询框架
  12. 【高电复习3】频谱的线性搬移、振幅调制、解调、混频
  13. 【合集】高等数学随堂笔记-苏德矿老师-部分内容汇总
  14. swagger 常用注解
  15. 华为云学院-人人学loT学习笔记- 第三章 窄带无线,宽带互联
  16. 微信公众号盈利模式_微信公众号的盈利方式有哪些?
  17. Windows和Ubuntu搭建局域网中共享文件夹
  18. 腾讯课堂电脑版麦克风怎么打开
  19. 电影《妖妖铃》改名为《妖铃铃》
  20. 港科夜闻|香港科大商学院5位教授跻身世界顶尖科学家之列

热门文章

  1. 没有Hyper-V服务,WP Emulator无法启动
  2. Linux软件安装小结
  3. Tizen 2.0 SDK 和源码发布
  4. 迪普工业以太网交换机产品线
  5. 我用过的,我正在用的,比较高效、好用的命令
  6. 包钢集团、云南建投、南粤交通等大型国企选择用友BIP 实现财务数智能化
  7. Spring could 使用Feign超时问题
  8. 我在老男孩学python的日子_day1
  9. C 语言与动态库相关基础知识
  10. Linux中常用C/C++一些头文件的作用