单精度浮点数和双精度浮点数举例_float默认保留几位小数「建议收藏」

单精度浮点数和双精度浮点数举例_float默认保留几位小数「建议收藏」内容导视:之前漏掉了小数对应的二进制,现补上

单精度浮点数和双精度浮点数举例_float默认保留几位小数「建议收藏」"

3.3 浮点数表示法

内容导视:

  • 小数对应的二进制
  • 科学计数法
  • 浮点数表示法
  • 最大值、最小值
  • 特殊值

3.3.1 小数对应的二进制

之前漏掉了小数对应的二进制,现补上。

二进制转十进制

从个位数开始向左计算,个位数乘以 2 的 0 次方,十位数乘以 2 的 1 次方,百位数乘以 2 的 2 次方…

从十分位开始向右计算,十分位乘以 2 的 -1 次方,百分位乘以 2 的 -2 次方…

然后将每个式子的结果相加。

例 1:0b101.11 转为十进制

最高位是百位,从 1 * 2 ^ 2 开始
最低位是百分位,到 1 * 2 ^ -2 结束
0b101.11 = 
	1 * 2 ^ 2
+   0 * 2 ^ 1	
+   1 * 2 ^ 0
+   1 * 2 ^ -1
+   1 * 2 ^ -2
= 4 + 1 + 0.5 + 0.25 = 5.75

例 2:0b111.01 转为十进制

0b111.01 = 
	1 * 2 ^ 2
+   1 * 2 ^ 1
+	1 * 2 ^ 0
+   0 * 2 ^ -1
+   1 * 2 ^ -2
= 4 + 2 + 1 + 0 + 0.25 = 7.25

可以看到小数部分都是由 0.5、0.25、0.125、0.0625… 等数组合表示,前面的系数 1 或 0,0.625 = 1 * 0.5 + 0 * 0.25 + 1 * 0.125,所以 0.625 对应的二进制为 0.101。

例 3:0100.4 转为十进制,八进制转为十进制,同上过程,将 2 换成 8。

0100.4 = 
	1 * 8 ^ 2
+ 	4 * 8 ^ -1
= 64 + 0.5 = 64.5

100 = 0000100,1.1 = 1.10000,补 0 是为了方便转换为其它进制,参考 3.1.2 二进制与八进制互转。

4 = 04 = 0b100 = 0b0100 = 0x4,1 * 80 + 4 * 8-1 = 1.5 = 01.4 = 0b1.100 = 0b1.1000 = 0x1.8

十进制转二进制

整数部分除以 2 得到商,再将商除以 2,如此反复,直到商为 0,然后将每步得到的余数倒过来,就是对应的二进制。

小数部分乘以 2 得到积,取整数部分,余下的部分再次乘以 2,重复步骤,直到余数为 0,取出的整数合并再添上前缀 0. 就是小数对应的二进制。

再将整数和小数部分对应的二进制合并。

使用二进制表示小数可能会有精度损失。

例 1:0.75 转为二进制

0.75 * 2 = 1.5,取 1,余 0.5
0.5 * 2 = 1,取 1,余 0
所以 0.75 对应的二进制为 0b0.11

例 2:3.4 转为二进制

整数部分 3 转为二进制为 0b11
小数部分 0.4
0.4 * 2 = 0.8,取 0,余 0.8
0.8 * 2 = 1.6,取 1,余 0.6
0.6 * 2 = 1.2,取 1,余 0.2
0.2 * 2 = 0.4,取 0,余 0.4
0.4 * 2 = 0.8,取 0,余 0.8
...
所以小数部分 0.4 对应的二进制为 0b0.0110 0110 0110... 的循环
3.4 对应的二进制为 0b11.0110 0110 0110...

例 3:123.123 转为二进制

123/2,商 61,余 1
61/2,商 30,余 1
30/2,商 15,余 0
15/2,商 7,余 1
7/2,商 3,余 1
3/2,商 1,余 1
1/2,商 0,余 1
所以 123 对应的二进制为 0b1111011
0.123 * 2 = 0.246,取 0,余 0.246
0.246 * 2 = 0.492,取 0,余 0.492
0.492 * 2 = 0.984,取 0,余 0.984
0.984 * 2 = 1.968,取 1,余 0.968
0.968 * 2 = 1.936,取 1,余 0.936
0.936 * 2 = 1.872,取 1,余 0.872
0.872 * 2 = 1.744,取 1,余 0.744
0.744 * 2 = 1.488,取 1,余 0.488
0.488 * 2 = 0.976,取 0,余 0.976
...
0.123 对应的二进制为 0b0.000111110...

所以 123.123 对应的二进制为 0b1111011.000111110…

3.3.2 科学计数法

十进制表示法(E)

1.4E-45 = 1.4 * 10-45,1.4 称为尾数(Mantissa),10 为基数、底数(Base),-45 为指数(Exponent),都是十进制表示。

尾数在 [1.0,10) 之间的表示方法称为 modified normalized form,正是 Java 所使用的。

尾数在 [0.0,1.0) 之间的表示方法称为 true normalized form。

例:

  • 3.4028235E38 = 3.4028235 * 1038

  • 999999 = 9.99999 * 105

  • 0b10010101 = 0b1.0010101 * 27(对于二进制而言,每乘以 2 就相当于小数点往右移动一位)

Java 中,当小数超出 [-9999999,9999999] 范围时,会使用科学计数法表示。

System.out.println(-10000000.0);// -1.0E7

思考控制台输出什么结果?

System.out.println(500e-2);

答:500e-2 = 500 * 10-2 = 500 / 102 = 5.0

科学计数法默认被当作 double 类型来处理,所以小数不能掉。

System.out.println(-10000000);
System.out.println(500E-7);

第 1 个不是小数,原样输出。第 2 个超过上述所说的范围,那使用科学计数法表示,尾数应在 [1.0,10) 之间,输出 5.0E-5

十六进制表示(P)

在十六进制表示中,2 为底数。

例 1:0xa.0p-1 转为十进制

0xa.0 转为十进制为 10
0xa.0p-1 = 10 * 2 ^ -1 = 10 / 2 = 5.0

例 2:0x19.0p-2 转为十进制

0x19.0 转十进制
0x19.0 = 
	1 * 16 ^ 1
+   9 * 16 ^ 0
= 16 + 9 = 25

0x19.0p-2 = 25 * 2 ^ -2 = 25 / 4 = 6.25

例 3:0x0.12p2 转为十进制

0x0.12 转十进制
0x0.12 = 
	1 * 16 ^ -1
+   2 * 16 ^ -2
= 1/16 + 2/256 = 0.0703125

0x0.12p2 = 0.0703125 * 2 ^ 2 = 0.28125

例 4:0b1.0010101 * 27 转为十进制

0b1.0010101 * 2 ^ 7 = 0b10010101 = 1 + 4 + 16 + 128 = 149
0b1.0010101 = 0b1 + 0b0.0010101 = 1 + 0.125 + 0.03125 + 0.0078125 = 1.1640625
1.1640625 * 2 ^ 7 = 1.1640625 * 128 = 149
0b1.00101010 = 0x1.2a = 1 + 2/16 + 10/256 = 1 + 0.125 + 0.0390625 = 1.1640625
1.1640625 * 2 ^ 7 = 149

3.3.3 浮点数表示法

浮点数如何表示小数?

分为四部分表示:符号、尾数、基数和指数。

例:0b1010.11101 * 210

符号:+,尾数:1010.11101,基数:2,指数:10。

浮点数有两种类型:单精度浮点数(float:32bit)和双精度浮点数(double:64bit)。

3.3.3-1 浮点数存储格式
类型 总长度 符号 阶码 尾数
float 32 bit 1 8 23
double 64 bit 1 11 52

符号

1 表示负,0 表示正。

阶码

也称指数位,因为指数有正、负,为了避免使用符号位,方便比较、排序,采用了 The Biased exponent(有偏指数)。[1]

IEEE754 规定,2e-1 – 1 的值是 0(e 是阶码部分的位数),小于这个值表示负数,大于这个值表示正数。因此,对于单精度浮点数而言,127 是 0;双精度浮点数中 1023 是 0。

假设指数为 -5,使用单精度浮点数表示时,应存储 -5 + 127 = 122 的二进制 0111 1010。

指数为 1023,使用双精度浮点数表示时,应存储 1023 + 1023 = 2046 的二进制 11111111110。

8 位可以表示的值:00000000 ~ 11111111 即 0 ~ 255,其中 00000000 和 11111111 被用作特殊情况,所以可用范围只有 1 ~ 254,用来表示负数,如果取 128 为 0,可以表示 -127 ~ 126,取 127 为 0,可以表示 -126 ~ 127,表示的数更大。

11 位可用范围:00000000001 ~ 11111111110 即 1 ~ 2046,取 1023 为 0,可以表示 -1022 ~ 1023。

尾数

一个小数既可以使用 0b1010.11101 * 210 表示,也可以使用 0b1.01011101 * 213 表示。

通过移位,将小数点前面的值固定为 1。IEEE754 称这种形式的浮点数为规范化浮点数(normal number)。

如 0b0.00101 = 0b1.01 * 2-3

因为规定第 1 位永远为 1,因此可以省略不存,这样尾数部分多了 1 位,只需存 01。

因此对于规范化浮点数,尾数其实比实际的多 1 位,也就是说单精度的是 24 位,双精度是 53 位。为了作区分,IEEE754 称这种尾数为 significand。

基数

默认为 2,不参与存储。


[1] 为什么使用移码表示阶码

工具网址

  • 进制转换

转载文章

  • https://baijiahao.baidu.com/s?id=1679268252794098534

3.3.4 一些例子

加空格只是为了更好的观察。

例 1:用 float 表示 329.301

329.301 的二进制为 0b101001001.01001101000011100101011000000100000110001001001101111… = 0b1.0100100101001101000011100101011000000100000110001001001101111… * 28

符号位:0

阶码:8 + 127 = 135,对应的二进制为:10000111

尾数取出 23 位:01001001 01001101 0000111(规格化表示,1 省略不存)

所以单精度浮点数 329.301 对应的二进制内存表示是:0 10000111 01001001010011010000111

3.3.4-1 float 内存表示对应的整数
// 329.301 的二进制内存表示对应的整数
int i = Float.floatToIntBits(329.301f);
// 329.301 的二进制内存表示
Object s = Integer.toBinaryString(i);
System.out.println(i);// 1134864007
System.out.println(s);// 1000011 10100100 10100110 10000111

// -1 的补码为 11111111 11111111 11111111 11111111
System.out.println(Integer.toBinaryString(-1));
// -1 的补码为 ffffffff
System.out.println(Integer.toHexString(-1));
// 加下划线不影响实际数值
System.out.println(0b11111111_11111111_11111111_11111111);
System.out.println(0xffffffff);

例 2:用 double 表示 -666.875

666.875 实际对应的二进制为 0b1010011010.111 = 0b1.010011010111 * 29

符号位:1

阶码:9 + 1023 = 1032,对应的二进制为:10000001000

尾数位:010011010111,不够 52 位补 0。

所以双精度浮点数 -666.875 对应的二进制内存表示是:1 10000001000 0100110101110000000000000000000000000000000000000000

3.3.4-2 double 内存表示对应的整数
long l = Double.doubleToLongBits(-666.875);
Object s = Long.toBinaryString(l);
// -4574294926501609472
System.out.println(l);
// 1100000010000100110101110000000000000000000000000000000000000000
System.out.println(s);

例 3:单精度浮点数内存表示:00111101110011001100110011001101,求它对应的小数

符号位:0,正数

阶码取 8 位:01111011,转为十进制 123,123 – 127 = -4,指数为 -4

尾数取 23 位:10011001100110011001101,尾数为 1.10011001100110011001101

小数的二进制表示为 0b1.10011001100110011001101 * 2-4 = 0b0.000110011001100110011001101

0b0.000110011001100110011001101 对应的小数:0.10000000149011612…,由于 float 精度最多只能表示 8 位,所以对应的小数是 0.1。

float v = Float.intBitsToFloat(0b00111101110011001100110011001101);
System.out.println(v);// 0.1

3.3.5 浮点数精度

浮点数的精度是指浮点数的有效数字的最大位数,从左边第一个不为 0 的数字开始的个数开始算起。

System.out.println(0.1111111111111f);//0.11111111

可以发现超过了 8 位后的数字都被舍弃,float 最多只能表示 8 位有效数字。

例 0b0.01010010 11101001 01010100 10101010 = 0b1.010010 11101001 01010100 10101010 * 2-2

使用单精度浮点数表示,只能存储 23 位尾数,也就是 010010 11101001 01010100 1,丢掉了一部分,导致精度损失。

大部分文章的解释

单精度浮点数尾数有 23 位,加上默认的 1,2 ^ (23+1)= 16777216。因为 10^7 < 16777216 < 10^8,所以说单精度浮点数的有效位数是 7 位,部分可以达到 8 位。

双精度浮点数尾数有 52 位,2 ^ (52+1)= 9007199254740992,10^15 < 9007199254740992 < 10^16,所以双精度的有效位数是 15 位,部分可以达到 16 位。

说实话,这个解释不大满意,之前的二、十进制转换,求余得到二进制,勉强可以不探究原理;这里就不太好理解,难道是指 24 位全取 1,是最大值,再大就表示不了的意思?只能被迫舍去一些位?

没办法只能自己做实验了,以单精度浮点数为例。

整数部分

23 = 0b10111 = 0b1.0111 * 24

如果尾数部分只能存 1 位,即 0,后面的 111 被舍弃,实际存储的值为 0b1.0 * 24 = 0b10000 = 16,那么连一位有效数字都保证不了。

9 = 0b1001 = 0b1.001 * 23,如果尾数只能存 1 位,即 0,后面的 01 被舍弃,实际存储的值为 0b1000 = 8;如果尾数有 3 位,精度才不会损失。

15 = 0b1111 = 0b1.111 * 23,要想精确表示,应存储 111,尾数至少 3 位。

要想表示 1 ~ 9 范围内的所有整数:0001 ~ 1001,需要 4 位二进制,尾数至少需要 3 位。

表示 10 ~ 99:1010 ~ 1100011,至少需要 6 位尾数,如 1100011 = 1.100011 * 26,存储 100011 可以保证精度不损失。

表示 106 ~ 107:11110100001001000000 ~ 100110001001011010000000,至少需要 23 位尾数。

24 位二进制能表示的最大值:16777215,超过了此数,如 16777217,0b1000000000000000000000001 = 0b1.000000000000000000000001,存储 23 位 0,第 24 位的 1 被略去,精度损失。

小数部分可参考:https://blog.csdn.net/pkxpp/article/details/103059502

意思大概是小数由 0.5、0.25、0.125、0.0625、0.003125、0.015625、…等单位组合而成。

如 0.625 = 1 * 0.5 + 0 * 0.25 + 1 * 0.125,则 0.625 = 0b0.101

如果要表示的数比最小单位还小,如用 0.5、0.25、0.125、0.0675、0.03125、0.015625 组合表示不了 0.001,精度只能到十分位 0.1,有时可以到百分位。

例,六位二进制表示 0.175,最贴近的是 0b0.001011 = 0.171875。

要想保证精确到小数点后面 3 位,2x < 10-3,求得 x的最大值为 -10,最小单位 0.0009765625,用十位二进制表示 0b0.0010110100 = 0.17578125。

23 位尾数,能够表示的最小单位 2-24 = 0.0000000590444775 < 10-7,可以精确到小数点后 7 位,部分 8 位。

小数部分的解释,我不大满意,因为有负指数的存在。

0.1 + 0.2 = ?

使用单精度浮点数表示;

0.1 = 0b0.0001 10011001 10011001 1001100 1100110011001100110011001101…

第 23 位是否进位,看后面的位数是否大于 10000000 00000000 00000000 0000010 00000000…剩下都是 0,设此二进制为 b。

这是小数部分,可以无限往后填充 0,按位比就行。

例,比较 11 与 b,第 1 位相等,都是 1;第 2 位的 1 大于 0,所以 11 更大。

如 0b1.11101010 10101010 1010111 1011,1011 > b,所以第 23 位进位 + 1,应存储 11101010 10101010 1011000,多余的 1011 被舍弃。

如 0b1.10100101 10101111 1111010 01,01 < b,应存储 10100101 10101111 1111010。

尾数存储 23 位,实际值为 0b0.0001 10011001 10011001 1001101 = 0.10000000149011612

0.2 = 0b0.001 10011001 10011001 1001100 1100110011001100110011001101…

实际值 0b0.001 10011001 10011001 1001101 = 0.20000000298023224

所以 0.1 + 0.2 = 0.10000000149011612 + 0.20000000298023224 = 0.30000000447034836

得到的结果大于 0.3,不过单精度浮点数由于精度不够,表示不出来。

System.out.println(0.1f + 0.2f);// 0.3
System.out.println(0.1 + 0.2);// 0.30000000000000004

3.3.6 最大值、最小值

单精度浮点数最大值

8 位可以表示的值:00000000 ~ 11111111 即 0 ~ 255,其中 00000000 和 11111111 被用作特殊情况,所以可用范围只有 1 ~ 254,用来表示负数,取 127 为 0,可以表示 -126 ~ 127。

单精度最大指数为 127,最小指数 -126。

双精度最大指数为 1023,最小指数 -1022。

由于阶码 0b11111111 被用作特殊情况,最大指数应为 0b11111110 – 127 = 254 – 127 = 127。

在内存中的表示:0 11111110 11111111111111111111111

1/2 + 1/4 = 1 – 1/4

1/2 + 1/4 + 1/8 = 1 – 1/8

1/2 + 1/4 + … + 1/223 = 1 – 1/223

单精度浮点数最大值为 0b1.11111111 11111111 1111111 * 2127 = 2127 + 2126 + … + 2104 = 2127 * [1 + 1/2 + 1/4 + … + 1/223] = 2127 * [1 + 1 – 1/223] = 2128 – 2104 = 3.4028235E38

0b1.11111111 11111111 1111111 * 2127 = 0x1.fffffe * 2127

单精度浮点数最小正值

使用规范化浮点数时,尾数默认省略了前导数 1,导致 0 无法表示。

即使尾数全部取 0,0b1.0000… * 2n = 1 * 2n,也无法精确表示 0。

所以规定了另一种浮点数:

当指数位全是 0 时,尾数部分的前导数为 0,同时指数部分的偏移值比规范形式的偏移值小 1。如单精度浮点数取 126 为 0。

最小指数 0b00000000 – 126 = -126。

最小正值在内存中的表示:0 00000000 00000000000000000000001

单精度浮点数最小正值为 0b0.0000 0000 0000 0000 0000 001 * 2-126 = 2-23 * 2-126 = 2-149 = 1.4e-45

0b0.0000 0000 0000 0000 0000 001 * 2-126 = 0x0.000002 * 2-126

双精度浮点数最大值

内存表示:0 11111111110 1111111111111111111111111111111111111111111111111111

指数的最大值为 0b11111111110 – 1023 = 2046 – 1023 = 1023

这里使用二进制表示太长,转为十六进制表示

最大值 0x1.fffffffffffff * 21023 = 21023 * [1 + 1 – 1/252] = 21024 – 2971 = 1.7976931348623157e308

双精度浮点数最小正值

内存表示:0 00000000000 0000000000000000000000000000000000000000000000000001

指数的最小值为 0b00000000000 – 1022 = -1022

最小正值 0x0.0000000000001 * 2-1022 = 2-52 * 2-1022 = 2-1074 = 4.9e-324

3.3.7 特殊值

为了表示 -1.0 / 0.0、1.0 / 0.0、0.0 / 0.0 等特殊数值,定义了三种类型 -Infinity(负无穷大)、Infinity(正无穷大)、NaN(非数字)。

无穷大

当指数位全为 1,尾数位全为 0,表示为无穷大,当一个数超出了浮点数的表示范围,就可以使用 Infinity 表示。符号为 0,是正无穷大,符号为 1,负无穷大。

单精度浮点数 Infinity 内存表示 0 11111111 0000000000000

双精度浮点数 -Infinity 内存表示 1 11111111111 0000000000000000000000000000000000000000000000000000

0b1111 1111 1111 0000000000000000000000000000000000000000000000000000 = 0xfff00…

// 0111 1111 1111 0000000...,是正无穷大
double d1 = Double.longBitsToDouble(0x7FF0000000000000L);
// 1111 1111 1000 00000...,是负无穷大
float f1 = Float.intBitsToFloat(0xFF800000);

非数字

符号位任意,指数位全为 1,尾数位不全为 0(至少有 1 位是 1),表示不是数,比如用 NaN 表示 -1 的平方根。

特性:

  • 不等于自身,可以利用此特性判断一个数是否是 NaN

    // 1 11111111 1110...
    // fff00000
    float f1 = Float.intBitsToFloat(0xfff00000);
    System.out.println(f1 == f1);// false
    
    public static boolean isNaN(float v) { 
         
        return (v != v);
    }
    
  • NaN 参与运算,最终结果还是 NaN

今天的文章单精度浮点数和双精度浮点数举例_float默认保留几位小数「建议收藏」分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:http://bianchenghao.cn/82355.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注