如何正确使用float和double?

qchen
• 阅读 1748

1、典型问题

问题一:条件判断超预期

System.out.println(1f == 0.9999999f)    // false
System.out.println(1f == 0.99999999f)   // true

问题二:数据转换超预期

float f = 1.1f;
double d = (double)f;
System.out.println(f);  // 1.1
System.out.println(d);  // 1.100000023841858

问题三:基本运算超预期

System.out.println( 0.2 + 0.7 ); // 0.8999999999999999 

问题四:数据自增超预期

float f1 = 8455263f;  // 7位数
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.0

float f2 = 84552631f;  // 8位数
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

2、浮点数的精度问题

浮点数在计算机中的存储方式遵循IEEE 754 浮点数计数标准,可以用科学计数法表示为: 如何正确使用float和double? 对于float和double两种浮点数在内存中的存储结构如下图所示: 如何正确使用float和double? 如何正确使用float和double? 1、符号部分(S)

  • 0-正
  • 1-负

2、阶码部分(E)(指数部分)

  • 对于float型浮点数,指数部分8位,考虑可正可负,因此可以表示的指数范围为-127 ~ 128
  • 对于double型浮点数,指数部分11位,考虑可正可负,因此可以表示的指数范围为-1023 ~ 1024

3、尾数部分(M) 浮点数的精度是由尾数的位数来决定的:

  • 对于float型浮点数,尾数部分23位,换算成十进制就是 2^23=8388608,所以十进制精度只有6 ~ 7位;
  • 对于double型浮点数,尾数部分52位,换算成十进制就是 2^52 = 4503599627370496,所以十进制精度只有15 ~ 16位

所以,上述0.99999999f超出float型浮点数据的精度范围。

3、如何解决精度问题

  • 用字符串或者数组解决多位数问题
  • Java的大数类:BigDecimal、BigInteger
    BigDecimal num3 = new BigDecimal( Double.toString( 1.0f ) );
    BigDecimal num4 = new BigDecimal( Double.toString( 0.99999999f ) );
    System.out.println( num3 == num4 );  // 打印 false
    

BigDecimal num1 = new BigDecimal( Double.toString( 0.2 ) ); BigDecimal num2 = new BigDecimal( Double.toString( 0.7 ) );

// 加 System.out.println( num1.add( num2 ) ); // 打印:0.9

// 减 System.out.println( num2.subtract( num1 ) ); // 打印:0.5

// 乘 System.out.println( num1.multiply( num2 ) ); // 打印:0.14

// 除 System.out.println( num2.divide( num1 ) ); // 打印:3.5

局限性:大数类的运算效率肯定是不如原生类型效率高,代价还是比较昂贵的,是否选用需要根据实际场景来评估。
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
翼
3年前
js 数组 转为树形结构
需要转换为树形的数组vardata{"orderById":null,"platformCommissionProportion":1,"name":"添加剂","pid":13,"id":26
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
qchen
qchen
Lv1
谁家玉笛暗飞声,散入春风满洛城。
文章
4
粉丝
2
获赞
8