float、double的精度、范围,在内存中的存储⽅式
float、double的精度,在内存中的存储⽅式
⼀、浮点型变量在内存中的存储⽅式
Java的浮点数遵循IEEE 754标准,采⽤⼆进制数据的科学计数法来表⽰浮点数,float遵从的是IEEE
R32.24 ,⽽double 遵从的是R64.53。该标准中表⽰的浮点数表⽰分为规约形式和⾮规约形式以及特殊情况。
⽆论是单精度还是双精度在存储中都分为三个部分:
1. 符号位(Sign) : 0代表正,1代表为负
2. 指数位(Exponent):⽤于存储科学计数法中的指数数据,并且采⽤移位存储
3. 尾数部分(Mantissa):尾数部分
根据IEEE 754 标准,对于float单精度,第 31 位(左边第1位)表⽰浮点数字的符号;第 30-23位(8位)表⽰指数(指数加完偏移量,即加偏移量127后的值);第 22-0 位是尾数(尾数是23位);存储⽅
式如下图所⽰:
根据IEEE 754 标准,对于double双精度,第 63 位表⽰浮点数字的符号;第 62-52 位(11位)表⽰指数(指数加完偏移量);第 51-0 位是尾数(尾数是52位,尾数位⽐float多,尾数位越多,精度越⾼);双精度的存储⽅式为:
指数部分与float单精度存储⽅式⼀样使⽤偏移(Offset)算法,存储的数据=元数据 + 1023,所以【实际指数值 = 指数部分⼆进制值 - 1023】。11位⼆进制能表⽰的范围为0~2047,所以指数部分能表⽰的范围为-1022~1023。
⾮规约形式表⽰:
当指数部分全0⽽且⼩数部分不全0时表⽰的是⾮规格化的浮点数,因
为这⾥默认没有前导1,⽽是0。
对于float类型,取值位0.f * 2-126,表⽰范围位 2-149~(1-2-23) × 2-126
这⾥没有考虑符号。(IEEE 754标准规定:⾮规约形式的浮点数的
指数偏移值⽐规约形式的浮点数的指数偏移值⼩1。)
其他特殊表⽰:
1.当指数部分和⼩数部分全为0时,表⽰0值,有+0和-0之分(符号位决
定),0x00000000表⽰正0,0x80000000表⽰负0.
2.指数部分全1,⼩数部分全0时,表⽰⽆穷⼤,有正⽆穷和负⽆
穷,0x7f800000表⽰正⽆穷,0xff800000表⽰负⽆穷.
3.指数部分全1,⼩数部分不全0时,表⽰NaN,分为QNaN和SNaN,Java
中都是NaN.
⼆、⼆进制的科学计数法
⼗进制的科学计数法:任何数字都可以表⽰成a×10n,其中1≤a<10,n表⽰整数,⼀般⽤于表⽰很⼤的数字,例如:623500000000可以表⽰为6.235×1011。同理,任何⼆进制都可以表⽰成a×2n,其中a为带⼩数点的⼆进制序列,且⼩数点在第⼆位,n表⽰整数,例如:100111101100000000000000可以表⽰为1.001111011×223。
三、⼗进制转换为⼆进制
1.整数部分
余数法:⽤这个⼗进制的整数除以 2,会得到⼀个商值和⼀个余数值,再⽤商除以 2,⼀直除到商为 0 为⽌,把每次的余数,逆序连起来,就是要转的⼆进制数。整数总能⽤⼆进制准确表⽰。
2.⼩数部分
⼩数部分就是⽤⼗进制⼩数乘以2,得出的积,然后把积的整数位取出,再⽤积的⼩数部分乘以2,再把积的整数位取出,再⽤⼩数部分乘以2,循环操作,直到⼩数部分为0,或者遇到⽆限循环(⼩数转换为⼆进制可能会损失精度),取到你认为⾜够精度的⼩数为⽌,然后把取出的整数位顺序连接起来,就是要转换成的⼆进制⼩数。
⼗进制⼆进制
0.50.1
0.250.01
0.1250.001
0.06250.0001
0.031250.00001
0.0156250.000001
0.00781250.0000001
0.003906250.00000001
⼗进制 0.875 转换成⼆进制 0.111,⼗进制0.3转换成⼆进制0.01001100110011…。
四、⼆进制转换为⼗进制
float与double的⼆进制表⽰转换为⼗进制时,先表⽰为科学计数法,再分别转换⼩数部分和整数部分。
0 01111111 00000000000000000000000的指数部分为01111111=(27-1)-127 = 0,尾数部分为00000000000000000000000,(因整数部分总为1,所以存储时省略整数部分)该数的科学计数法表⽰为1. 00000000000000000000000×20,所以该⼆进制对应的⼗进制为1。
1 10000000 00000000000000000000000的指数部分为10000000 = 27-127 = 1,尾数部分为00000000000000000000000,该数的科学计数法表⽰为1.00000000000000000000000×21,左移1位变为10.0000000000000000000000,所以该⼆进制对应的⼗进制为2。
0 01111101 00110011001100110011001的指数部分为01111101 = 125 – 127 = -2,尾数部分为00110011001100110011100,该数的科学计数法表⽰为1.00110011001100110011100×2-2,右移2位变为0.0100110011001100110011001,整数部分为0,⼩数部分约为0.3,该数为0.3。
0 10000000 10010010000111111011011的指数部分为10000000 = 27 – 127 = 1,尾数部分为10010010000111111011011,该数的科学计数法表⽰为1.10010010000111111011011×21,左移1位变为11.0010010000111111011011,整数部分为3,⼩数部分约为0. 1415926,该数为3.1415926。
五、float、double的精度
数学领域中的精度⼀般指有效数字,是⼗进制位数,⽽计算机中的精度通常是指⼆进制位数。
从上述⼏个float的⼆进制表⽰转换为⼗进制的⽰例可以看出:
1. 指数位决定了范围⼤⼩,因为指数位表⽰的越⼤则表⽰的数值越⼤。
2. 尾数位决定了计算精度,因为尾数位能表⽰的越⼤,则计算精度越⼤。
浮点数的精度决定于尾数部分,⽽float尾数占了23个⼆进制位,加上省略的整数部分的1,共24位决定浮点数的精度。24位⼆进制表⽰的最⼤数字为224转化为⼗进制数为 16,777,216(8个⼗进制位),因此有⼀种说法是float的⼗进制精度为8位,但是由于其并不能表⽰所有8位⼗进制数,因此也有种说法是其精度为7位。【这些说法都不准确,因为尾数部分包含整数部分和⼩数部分】准确的说,float可以保证7位⼗进制有效数字。
float能表⽰的最⼤数为0 11111110 11111111111111111111111,也是Float.MAX_VALUE的值,(224-1)× 2(127-23)约为3.4028235E38。
float能表⽰最⼩正数为1 00000000 00000000000000000000001(⾮规约表⽰),也是Float.MIN_VALUE的值,2-149约为1.4E-45
double位数占52位,加上省略的整数部分的1,共53位决定浮点数的精度。53位⼆进制表⽰的最⼤数字为253转化为⼗进制数为9,007,199,254,740,992(16个⼗进制位),但是它不能表⽰所有16位⼗进制数,其精度介于15~16位。准确的说,double可以保证15位⼗进制有效数字。
Double表⽰的最⼤数为1.7976931348623157E308,最⼩正数为4.9E-324
六、浮点转⼆进制科学表⽰的代码实现
1import java.util.Scanner;
2import *;
3public class  FloatToHex {
4/**
5*将⽤户输⼊的浮点数,转换为⼆进制科学计数形式(浮点数在内存中的存储⽅式)
6*@author:    李世颖
7*@Create Date: 2020-01-10
8*/
9public static void main(String[] args) {
10// 键盘输⼊
11          Scanner sc = new Scanner(System.in);
12          String cmd=null;
13float f  = 0;
14//double d = 0;
15        String binaryStr = "";
16int prefixLen = 0;//需要补0位的数量
17//float类型正则表达式规则
18          Pattern p = Patternpile("-?\\d+\\.?\\d*");
19          Matcher m = null;
20//提⽰输⼊
21          System.out.println("输⼊浮点数,转换输出该浮点数的内存⼆进制表⽰形式。");
22while (sc.hasNext()) {
23//获取输⼊并删除分隔符
24                cmd = sc.nextLine();
25//退出命令
26if (cmd.equalsIgnoreCase("exit")) {
27                      sc.close();
28return;
29                }浮点型变量float
30//判断输⼊是否合法
31                m = p.matcher(cmd);
32if (m.matches()) {
33try {
34                            f = Float.parseFloat(cmd);
35//d = Double.parseDouble(cmd);
36                            System.out.println(cmd + "在内存中的⼆进制表⽰如下:");
37                            binaryStr = BinaryString(Float.floatToIntBits(f));//将float转换为⼆进制字符串
38//binaryStr = BinaryString(Double.doubleToLongBits(d));
39                            prefixLen = 32 - binaryStr.length();
40//prefixLen = 64 - binaryStr.length();
41if(prefixLen > 0){
42                                  System.out.println(String.format("%0"+prefixLen+"d",0) + binaryStr);//补0后输出
43                            }else{
44                                System.out.println(binaryStr);
45                            }
46                      } catch (Exception e) {
47                            System.out.println("输⼊的浮点数不合法或超出浮点数的范围"); 48continue;
49                      }
50                } else {
51                      System.out.println("请输⼊合法的浮点数");
52                }
53          }
54    }
55 }