Java 浮点数的范围和精度

本篇先介绍IEEE754标准中针对浮点数的规范,然后以问答形式补充有关浮点数的知识点。

(一)IEEE754标准

IEEE 754 标准即IEEE浮点数算术标准,由美国电气电子工程师学会(IEEE)计算机学会旗下的微处理器标准委员会发布。

以32位float数据为例,在内存中的存储形式是1bit的符号位(S),8bit表示指数部分(Exp),23表示小数部分的尾数(Fraction)。

表一 单精度浮点数在内存中存储形式


1bit符号


8bit指数部分


23bit尾数

符号位——S取0时表示负数,取1时表示负数。

指数部分——使用所谓的偏正值形式表示,而不是补码表示,即指数部分采用一个无符号的正数值存储。也就是说指数部分要表示的值等于实际存储值减去一个固定值(对于单精度float类型,是127)。采用这种方式表示的目的是简化比较,因为,如果采用补码表示的话,全体符号位S和Exp自身的符号位将导致不能简单的进行大小比较。因为指数值的大小从0~255(0和255是特殊值),单精度的指数部分范围是-127~+128(对应的,-127和128是特殊值)。

尾数部分——23bit尾数仅能表示小数部分的尾数,小数部分最高有效位由指数部分决定,具体见下表。小数部分最高有效位是1的数被称为正规形式。小数部分最高有效位是0的数被称为非正规形式,其他情况是特殊值。

表二 单精度浮点数表示规则


符号


指数

部分


指数部分-127


尾数部分


小数部分的

最高有效位


形式


1


255


128


非0


没有


NaN


1


255


128


0


没有


负无穷


1


1~254


-126~127


任意


1


正规形式(负数)


1


0


-127


非0


0


非正规形式(负数)


1


0


-127


0


没有


负0


0


0


-127


0


没有


正0


0


0


-127


非0


0


非正规形式(正数)


0


1~254


-126~127


任意


1


正规形式(正数)


0


255


128


0


没有


正无穷


0


255


128


非0


没有


NaN

按照IEEE标准,除了 NaN 以外,浮点数集合中的所有元素都是有序的。如果把它们从小到大按顺序排列好,那顺序将会是:负无穷,正规形式(负数)、非正规形式(负数)、负0、正0、非正规形式(正数)、正规形式(正数)、正无穷。

对于64bit的双精度double类型,在内存中的存储形式是1bit的符号位(S),11bit表示指数部分(Exp),52bit表示小数部分的尾数(Fraction)。指数部分的偏正值是1023,其他情况跟单精度类似,不再赘述。

(二)浮点数Q&A

1)浮点数可以表示数据的范围是什么?

不考虑特殊值(无穷大、NaN等),浮点数可以表示的范围是[-Max,Max]。其中Max是浮点数能表示的最大值,具体值参见表三。

表三 浮点数最大值


浮点类型


字节码


16进制表示


10进制表示


单精度


7f7fffff


0x1.fffffep127


3.4028235E38


双精度


7fefffffffffffff


0x1.fffffffffffffp1023


1.7976931348623157E308

2)浮点数的精度怎样衡量?

浮点数指数部分Exp的数值决定了浮点数与相邻浮点数的差值,所以,指数部分越小(单精度最小为-127),即浮点数绝对值越小(也就是浮点数越靠近0),相邻浮点数的差值越小(单精度最小为2^(-127)),浮点数能表示的有效小数位数越多。反之,指数部分越大(单精度最大为127),即浮点数绝对值越大(也就是浮点数越远离0),相邻浮点数的差值越大(单精度最小为2^(127)),浮点数能表示的有效小数位数越少。但是,从科学计算的角度看,不管指数部分的数值是多少,浮点数的有效位数由尾数部分决定,单精度的有效数是7位,双精度的有效数是16位。

表四 浮点数最小正数


浮点类型


字节码


16进制表示


10进制表示


单精度最小正数


00000001


0x0.000002p-126


1.4E-45


双精度最小正数


0000000000000001


0x0.0000000000001p-1022


4.9E-324

3)我们知道,在Java中,存在基本数据类型的自动转换,比如,直接将一个整形字面量赋给一个float变量。 那么,在自动转换后,整形的精度会丢失么?

当浮点集中没有与整形值对应的浮点数时,会将整形值转化成最接近的浮点值,此时,整形值会丢失精度。例如下面的例子,数值为33554431的整形转化成单精度浮点数后,变成3.3554432E7,即33554432。

int intValue = Integer.MAX_VALUE >> 6;// 33554431

float floatFromInt = intValue;

System.out.println(floatFromInt);// 3.3554432E7

System.out.println(intValue);// 33554431

最后附查看某些浮点数字节码、16进制表示、10进制表示的源码及运行结果。

单精度源码及结果。

 1 package com.wsm.test;
 2
 3 public class TestFloat {
 4
 5     /**
 6      * @param args
 7      */
 8     public static void main(String[] args) {
 9
10         int intValue = Integer.MAX_VALUE >> 6;// 33554431
11         float floatFromInt = intValue;
12         System.out.println(floatFromInt);// 3.3554432E7
13         System.out.println(intValue);// 33554431
14
15         System.out.printf("%-12s\t%-20s%-12s%-20s\n", "描述", "十六进制数",
16                 "        字节码", "            十进制数");
17         print("正        无         穷", Float.POSITIVE_INFINITY);
18         print("最        大         值", Float.MAX_VALUE);
19         print("最小正规形式正数", Float.MIN_NORMAL);
20         print("最大非正规形式值", +0x0.fffffep-127f);
21         print("最    小      正    数", Float.MIN_VALUE);
22         print("负         无        穷", Float.NEGATIVE_INFINITY);
23         print("规     范     的 NaN", Float.NaN);
24         print("其     他     的 NaN", Float.intBitsToFloat(Integer
25                 .valueOf(0xffc54321)));
26
27     }
28
29     static void print(String describe, float floatNum) {
30         System.out.printf("%-12s\t%-20s%-12s%-20s\n", describe, Float
31                 .toHexString(floatNum), insertZero(Integer.toHexString(Float
32                 .floatToRawIntBits(floatNum)), 8), floatNum);
33     }
34
35     static String insertZero(String input, int length) {
36         StringBuilder sb = new StringBuilder(input);
37         while (sb.length() < length) {
38             sb.insert(0, "0");
39         }
40         return sb.toString();
41     }
42 }

Float

3.3554432E7
33554431
描述           十六进制数                       字节码             十进制数   
正        无         穷 Infinity            7f800000    Infinity           
最        大         值 0x1.fffffep127      7f7fffff    3.4028235E38       
最小正规形式正数     0x1.0p-126          00800000    1.17549435E-38     
最大非正规形式值     0x0.8p-126          00400000    5.877472E-39       
最    小      正    数 0x0.000002p-126     00000001    1.4E-45            
负         无        穷 -Infinity           ff800000    -Infinity          
规     范     的 NaN NaN                 7fc00000    NaN                
其     他     的 NaN NaN                 ffc54321    NaN

双精度源码及结果。

 1 package com.wsm.test;
 2
 3 public class TestDouble {
 4
 5     /**
 6      * @param args
 7      */
 8     public static void main(String[] args) {
 9
10         System.out.printf("%-12s\t%-30s%-24s%-40s\n", "描述", "十六进制数",
11                 "                  字节码",
12                 "                                   十进制数");
13         print("正        无         穷", Double.POSITIVE_INFINITY);
14         print("最        大         值", Double.MAX_VALUE);
15         print("最小正规形式正数", Double.MIN_NORMAL);
16         print("最大非正规形式值", +0x0.fffffffffffffp-1023d);
17         print("最    小      正    数", Double.MIN_VALUE);
18         print("负         无        穷", Double.NEGATIVE_INFINITY);
19         print("规     范     的 NaN", Double.NaN);
20         print("其     他     的 NaN", Double.longBitsToDouble(Long
21                 .valueOf(0xfff8000000054321L)));
22
23     }
24
25     static void print(String describe, Double DoubleNum) {
26         System.out.printf("%-12s\t%-30s%-24s%-40s\n", describe, Double
27                 .toHexString(DoubleNum), insertZero(Long.toHexString(Double
28                 .doubleToRawLongBits(DoubleNum)), 16), DoubleNum);
29     }
30
31     static String insertZero(String input, int length) {
32         StringBuilder sb = new StringBuilder(input);
33         while (sb.length() < length) {
34             sb.insert(0, "0");
35         }
36         return sb.toString();
37     }
38 }

Double

描述           十六进制数                                           字节码                                      十进制数
正        无         穷 Infinity                      7ff0000000000000        Infinity                               
最        大         值 0x1.fffffffffffffp1023        7fefffffffffffff        1.7976931348623157E308                 
最小正规形式正数     0x1.0p-1022                   0010000000000000        2.2250738585072014E-308                
最大非正规形式值     0x0.8p-1022                   0008000000000000        1.1125369292536007E-308                
最    小      正    数 0x0.0000000000001p-1022       0000000000000001        4.9E-324                               
负         无        穷 -Infinity                     fff0000000000000        -Infinity                              
规     范     的 NaN NaN                           7ff8000000000000        NaN                                    
其     他     的 NaN NaN                           fff8000000054321        NaN

参考资料:

1、IEEE754百度百科

2、《Java虚拟机规范(Java SE 7)》

时间: 2024-08-03 03:10:35

Java 浮点数的范围和精度的相关文章

Java 浮点数精度丢失

Java 浮点数精度丢失 问题引入 昨天帮室友写一个模拟发红包抢红包的程序时,对金额统一使用的 double 来建模,结果发现在实际运行时程序的结果在数值上总是有细微的误差,程序运行的截图: 输入依次为:红包个数,抢红包的人数,选择固定金额红包还是随机金额红包,每个红包的金额(此例只有一个红包). 注意到程序最后的结果是有问题的,我们只有一个金额为 10 的红包,一个人去抢,所以正确结果应该为这个人抢到了 10 RMB. 为了使问题更加明显,我们测试一个更加简单的例子: public class

java浮点数剖析

定点数表达法的缺点在于其形式过于僵硬,固定的小数点位置决定了固定位数的整数部分和小数部分,不利于同时表达特别大的数或者特别小的数.计算机系统采纳了所谓的浮点数表达方式.这种表达方式利用科学计数法来表达实数,即用一个尾数(Mantissa也叫有效数字 ),一个基数(Base),一个指数(Exponent)以及 一个表示正负的符号来表达实数.浮点数利用指数达到了浮动小数点的效果,从而可以灵活地表达更大范围的实数. 当一个浮点数的尾数为0,不论其阶码为何值,该浮点数的值都为0.当阶码的值为它能表示的最

Java中浮点类型的精度问题 double float

要说清楚Java浮点数的取值范围与其精度,必须先了解浮点数的表示方法与浮点数的结构组成.因为机器只认识01,你想表示小数,你要机器认识小数点这个东西,必须采用某种方法.比如,简单点的,float四个字节,前两个字节表示整数位,后两个字节表示小数位(这就是一种规则标准),这样就组成一个浮点数.而Java中浮点数采用的是IEEE 754标准. IEEE 754 标准 更多详见:https://baike.baidu.com/item/IEEE%20754 IEEE 754 标准是IEEE二进位浮点数

js,java,浮点数运算错误及应对方法

js,java,浮点数运算错误及应对方法 一,浮点数为什么会有运算错误 IEEE 754 标准规定了计算机程序设计环境中的二进制和十进制的浮点数自述的交换.算术格式以及方法. 现有存储介质都是2进制.2进制的进制基数是2,那么一个数字只要被因素包含大于2的质数的数除,都会产生无限循环小数.无限循环小数和无理数都无法,和非无限循环的有理数一起用同一种方式存储到存储介质上的同时还保持计算的兼容性. 对于无限循环小数,可以设计一种格式存储到介质上,但是同时又要和非无限循环的有理数能够计算,效率应该会变

JAVA 浮点数转化为百分数,分离整数和小数部分

JAVA 浮点数转化为百分数 public class DoubleToPercentformat { /** * 将double类型数据转换为百分比格式,并保留小数点前IntegerDigits位和小数点后FractionDigits位 * @param d * @param IntegerDigits * @param FractionDigits * @return */ public static String getPercentFormat(double d,int IntegerD

java中两double相加精度丢失问题及解决方法

在讨论两位double数0.1和0.2相加时,毫无疑问他们相加的结果是0.2.但是问题总是如此吗? 下面我们让下面两个doubles数相加,然后看看输出结果: @Test public void testBig(){ System.out.println(0.11+2001299.32); } 控制台输出2001299.4300000002 我们吃惊的发现,结果并不是我们预想的那样,这是为什么呢?又如何解决呢? 现贴出BigDecimal的一个构造函数的文档供大家参考 BigDecimal pu

Java浮点数相等性的判断

Java浮点数相等性的判断 问题描述如下: 给定两个变量double a.double b,它们对应的装箱类型变量分别是Double x.Double y,问: 是否存在一组a.b.x.y,满足 a==b && !x.equals(y) ? 是否存在一组a.b.x.y,满足 a!=b && x.equals(y) ? 乍看之下似乎是不可能的,实际上确实存在这样的值,参考以下代码 public static void main() { double a = 0.0; doub

Java 浮点数 float或double类型的表示范围和精度

隐约记得,浮点数判断大小好像有陷阱,因为底层的二进制数不能精确表示所有的小数.有时候会产生让人觉得莫名其妙的事情. 如在java中, 0.99999999f==1f //true 0.9f==1f //false 要明白这些,首先要搞清楚float和double在内存结构 1.内存结构 float和double的范围是由指数的位数来决定的. float的指数位有8位,而double的指数位有11位,分布如下: float: 1bit(符号位) 8bits(指数位) 23bits(尾数位) dou

Java浮点数float和double精确计算的精度误差问题总结

1.float整数计算误差 案例:会员积分字段采用float类型,导致计算会员积分时,7位整数的数据计算结果出现误差. 原因:超出float精度范围,无法精确计算. float和double的精度是由尾数的位数来决定的.浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响. float:2^23 = 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字: double:2^5