前言

  • 前段时间开发新的微信小程序,借此机会将老掉牙的支付模块重构,并且支持现金支付(之前都是虚拟币支付),在重构期间遇到计算上的一些精度问题,虽然数额影响非常小但是影响比较大,我觉得有必要总结以下遇到的一些问题,并且解决弄清楚他的原来,因此有下文。

先看几个现象

  • 当我们程序中涉及到一些double或者float类型的数据,并且精度要求比较高,小数点位数比较多的时候,可能会出现一些非常奇葩的问题,我下面针对遇到的一些问题给出几个说明案例:
条件判断异常
//比较特殊的案例
System.out.println(1f == 0.9999999f);   //false
System.out.println(1f == 0.99999999f);   //true
数据转换异常
float f = 0.6f;
double d1 = 0.6d;
double d2 = f;
System.out.println((d1 == d2) + "  " + f + "    " + d2);//false  0.6
基本运算异常
 System.out.println( 0.2 + 0.7 ); //0.8999999999999999
数据自增异常
   float f1 = 8455263f;for (int i = 0; i < 10; i++) {System.out.println(f1);f1++;}
//8455263.0
//8455264.0
//8455265.0
//8455266.0
//8455267.0
//8455268.0
//8455269.0
//8455270.0
//8455271.0
//8455272.0float f2 = 84552631f;for (int i = 0; i < 10; i++) {System.out.println(f2);f2++;}
//8.4552632E7
//8.4552632E7
//8.4552632E7
//8.4552632E7
//8.4552632E7
//8.4552632E7
//8.4552632E7
//8.4552632E7
//8.4552632E7
//8.4552632E7

Java中浮点类型精度问题

  • 要搞清除还是得从java的double和float类型来入手,我们都知道,计算机只认识二进制的0,1,那么在double和float中有整数部分,小数部分,如此说来那么应该会有一定的方式将小数转为计算机认识的0,1 组合,这中方法是定义的一个转换标准,其实而Java中浮点数采用的是IEEE 754标准
IEEE745 标准
  • IEEE 745 是IEEE二进制浮点数算数标准(Standard for Floating-Point Arthmetic)的标准编号,等同一个国际标准ISO之类,是美国哪家公司订的,这个标准定义来表示浮点数的格式(包括负零-0)与反常值(denormal number),一些特殊数值(例如无穷inf,非数值NaN)以及一些数值的“浮点数运算子”它知名来四种数值修约规则和五种例外状况。还有要了解的可以去JDK官网

浮点数的组成结构

  • 我们学习计算机组成原理的时候,应该学过的,Java中表示小数的时候有三个组成部分:

    • 符号位 S
    • 阶码部分 E
    • 尾数部分 M
  • 这三个纬度的信息,一个浮点数表示就可以完全确认下来,如下图所示的存储结构
  • 符号位部分 S: 0 表示正数, 1 表示负数
  • 阶码部分E (只整数部分):
    • 对于float型浮点数,指数部分8 位,考虑可正负,因此可以表示的指数范围是-127~128
    • 对于double类型浮点数,指数部分11 为,可正负,因此可以表示的指数范围是-1203~1024
  • 尾数部分 M:
    • 对于float类型来说,尾数23 为,计算成十进制就是2^23 = 8388608,所以十进制精度只有6~7位
    • 对于double类型来说,尾数部分52位,计算成十进制就是2^52=4503599627370496,所以十进制的精度是15~16位
  • 以上几个都是官方的数据
总结
  • 浮点数float和double在内存中是按照科学计数法来存储的,取值范围是由指数的位数来决定的,精度是由尾数的位数来决定的。
浮点数 精度/位数 符号S 指数E 扩展范围 (指数的取值范围) 最大/小值(取值范围) 尾数位M 尾数取值范围(精度)
float 32bit 单精度 1bit(0正1负) 8bit -27 ~ 27-1(-128~127) 2127(1038级别的数) 23bit 8388608,7位,精度为6~7位
double 64bit双精度 1bit(0正1负) 11bits -210 ~ 210-1(-1024~1023) 21023(10308级别的数) 52bit 45035_99627_37049_6,16位,精度为15~16位

浮点数和二进制数互相转化

十进制浮点数如何用二进制表示
  • 计算过程,小数部分,将小数部分乘以2,取出结果中整数部分作为二进制表示的第一位(大于等于1为1,小于1 为0),然后在将小数部分乘以2,得到整数部分作为二进制表示第二位…依次类推知道小数部分位0.
  • 特殊情况永远都不会位0:小数部分循环出现,无法停止,则用优先的二进制位无法表示这个小数,这也是在编程语言中小数位出现误差的原因。
  • 我们用如下的案例来说明这个过程10.6:
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 ---- 1
0.2*2=0.4 ---- 0
.
.
.
0.6*2=1.2 ---- 1
0.2*2=0.4 ---- 0
  • 以上我们可以发现0.6 是一个无法精确表示的一个数值,用二进制表示1001 1001 1001 1001 …
  • 那10.6 的二进制我们可以表示如下:1010.1001 1001 1001 …
二进制浮点数如何转为十进制
  • 计算过程:从左到右,v[i]*2(-i), i为从左到右的index,v[i]为该位的值,直接看例子如下:
  • 10.6 的二进制1010.1001 1001 1001,从小数点位为基准如下:
  • 0 * 20 + 1 * 21 + 0 * 22 + 1 * 23 = 0+2+0+8//(整数部分)
  • 1 * 2-1 + 0 * 2-2 + 0 * 2-3 + 1 * 2-4 + 1 * 2-5 + 0 * 2-6 + 0 * 2-7 + 1 * 2-8 … = 0.5+0.0625 + 0.03125 ≈ 0.6 // 小数部分

问题解答

  • 我们通过开始的案例,还有之前关于double,float这部分的分析,我们来解答一下最开始的哪些问题是怎么出现的:
float类型赋值给double类型变量出现精度问题
  • 因为float的尾数23为,double尾数52位,所以float类型中保存的0.6 的二进制转成double二进制的时候低位的二进制自动变成0 ,与用double类型保存的0.6的二进制是不一样的,所以出问题来,如下图来解释:
float 类型的0.6f:
1001 1001 1001 1001 1001 100
double类型的d1 = 0.6d:
1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001
将float类型f 赋值给double类型的d2后,d2 的实际数据位:
1001 1001 1001 1001 1001 1000 0000 0000 0000 0000 0000 0000 0000
  • 如上如果用d2 和d1 比较他们肯定是不相等的
第一个案例分析
System.out.println( 1f == 0.99999999f );
  • 这个结果是true因为计算机无法区分这个两个的二进制数,我们也来推到一些他们的二进制表示
1.0(十进制)↓
00111111 10000000 00000000 00000000(二进制)↓
0x3F800000(十六进制)0.99999999(十进制)↓
00111111 10000000 00000000 00000000(二进制)↓
0x3F800000(十六进制)0.9999999(十进制)↓
00111111 01111111 11111111 11111110(二进制)↓
0x3F7FFFFE(十六进制)
  • 如上,这第一个和第二个二进制数是一样的,第三个是不一样的,只是因为上面的0.99999999f 九个9 明显超过来float类型的精度范围凑巧和1 是一样的就出现这种问题。
浮点计算
  • Java当中默认声明的小数是double类型的,其默认后缀"d"或"D"可以省略,如果要声明为float类型,需显示添加后缀"f"或"F"
  • 我们尽量用BigDecial来计算

支付价格计算中精度问题之double,float相关推荐

  1. Java 中各数字类型(Double, Float, Long, Integer) 之间以及字符串之间的转换

    1. 数字类型间的转换 类型 double, float, long, int 都是 Java 中原始的数字类型. Java 提供了这原始类型的包装类, 并提供了一些便于使用的方法比如类型的转换, 通 ...

  2. c语言中如何用程序判断double型的浮点数能精确到几位小数,C语言中浮点数double/float相等判断...

    #include #include /* fabs */ #ifdef _WIN32 // #include #endif //输出的数值不断递增,即使将10改成10.0,循环也没有中止,为什么? v ...

  3. c#语言float转换int,【已解决】C#中double/float转int,小数部分四舍五入

    [问题] C#中,需要将一个double的值转换为int类型. [解决过程] 1.参考: 去试了试Math的Floor和Ceiling,已经Convert.Int64和Int32,测试结果见代码:pr ...

  4. c++ double float 数值比较

    浮点数在内存中的存储机制和整型数不同,其有舍入误差,在计算机中用近似表示任意某个实数.具体的说,这个实数由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2)的整数次幂得到,这种表示方法类似于基 ...

  5. Java 中浮点数---------BigDecimal和double(初探)

    为什么要使用 bigdecimal? 借用<Effactive Java>这本书中的话,float和double类型的主要设计目标是为了科学计算和工程计算.他们执行二进制浮点运算,这是为了 ...

  6. mysql float 精度阶段_mysql下float类型使用一些误差详解

    单精度浮点数用4字节(32bit)表示浮点数 采用IEEE754标准的计算机浮点数,在内部是用二进制表示的 如:7.22用32位二进制是表示不下的. 所以就不精确了. mysql中float数据类型的 ...

  7. java判断float相等_在java中判断两个浮点型(float)数据是否相等的案例

    示例代码: public static void main(String[] args) { float a = 10.222222225f; float b = 10.222222229f; Sys ...

  8. double float区别 java,float和double有什么区别?

    我已经读过双精度和单精度之间的区别. 但是,在大多数情况下, float和double float似乎是可互换的,即,使用一个或另一个似乎不影响结果. 真的是这样吗? 花车和双打何时可以互换? 它们之 ...

  9. char double java_java从入门到精髓 - Number char double float

    你的位置: 技术文档 -> C/C++/Java -> 文档详情 java从入门到精髓 - Number char double float public class MyNumber { ...

最新文章

  1. 光纤跳线接口的种类及适用范围
  2. 鸿蒙系统的逻辑,鸿蒙系统逻辑近似苹果iOS13?这华为在搞什么?
  3. 开启php的ssl,php怎么开启ssl?开启ssl的方法
  4. python画各种统计图的特点_Python 分词并画出词频统计图 | 睿鑫网络
  5. NGINX介绍及参数
  6. SQL Server如何制造大量测试数据
  7. 神经网络简介及简单应用
  8. 为资产分类定义折旧范围_SAP FICO-AA资产知识要点.doc
  9. 大专计算机课教案,计算机课教案
  10. 关于AD域和Exchange邮件服务器的学习总结
  11. 区块链报告会心得体会3000_区块链讲座观后感6
  12. 300英雄11月服务器维护,300英雄11月7日更新了什么?300英雄11月7日更新内容一览...
  13. Caffe:图像数据转换成ldb(leveldb/lmdb)文件
  14. 玩转f#的一个实例——解拼图游戏
  15. Java加密技术(一)—— HMACSHA1 加密算法
  16. 高项、高级项目管理师论文-进度管理
  17. 电巢:2022年半导体业国产替代现状(附产业图谱1000+企业名单)
  18. mysql 姓刘或姓李_MYSQL数据库查询
  19. 《国际服务贸易》期末复习题 及答案参考
  20. 苹果手机计算机找不到了,找不到apple mobile device

热门文章

  1. linux环境下用TcpDump抓包分析总结
  2. 安装bigdesk后es无法启动_安装天正后,探索者无法双击启动?
  3. 华为鸿蒙OS 2.0正式发布!明年华为手机将全面支持!
  4. 和哪个专业的男生谈恋爱最惨?
  5. 来看一场 AI 重建的 3D 全息世界杯比赛!
  6. clickhouse建库_ClickHouse高性能数据库
  7. python input输入多个变量_「Python 秘籍」1.2 解压可迭代对象赋值给多个变量
  8. Java 泛型 泛型的约束与局限性
  9. 登录服务器修改数据库吗,如何修改服务器登录数据库 sa
  10. 奥的斯服务器状态显示,奥的斯电梯调试服务器(俗称st)按键介绍