首页 > 其他分享 >浮点数编码原理

浮点数编码原理

时间:2023-03-19 16:57:08浏览次数:52  
标签:编码 二进制 浮点数 System println 原理 out

1 前言

​ 计算机中浮点数的编码,由美国加州大学的 William Kahan 教授于 1985 年设计,后被 IEEE 借鉴,制定出 IEEE 浮点标准。

​ 浮点数在计算机中的二进制编码由符号位(S)、阶码(或指数 E)、尾数(或基数 M)组成。单精度浮点数组成:1S + 8E + 23M,双精度浮点数组成:1S + 11E + 52M.

​ 通过如下方式可以打印浮点数的二进制编码:

System.out.println(Integer.toBinaryString(Float.floatToIntBits(f)));
System.out.println(Integer.toBinaryString(Double.doubleToLongBits(d)));

​ 使用在线进制转换器工具,可以快速进行10进制与2进制的转换。

2 思考

​ 在介绍浮点数存储原理之前,先列举几个 “神奇” 的案例。

//案例 1
System.out.println((123456725f - 3f > 123456724f)); // true
System.out.println((123456728f - 3f > 123456720f)); // true

//案例 2
System.out.println((123456724f + 4f < 123456725f)); // true
System.out.println((123456720f + 4f < 123456728f)); // true

//案例 3
int n = 123456789;
float s = 0f;
for (int i = 0; i < n; i++) {
    s += 1f;
}
System.out.println(s); // 16777216

​ 经常说不要使用浮点数进行 “==” 运算,但是案例 1 和案例 2 中,“>” 和 “<” 运算都出错了,案例 3 中最普通的累加运算也出错了,这使人怀疑,浮点数靠谱么?在了解浮点数的编码原理后,在一定的范围内使用浮点数,还是靠谱的。

3 编码原理

​ 浮点数在计算机中的二进制编码由符号位(S)、阶码(或指数 E)、尾数(或基数 M)组成。单精度浮点数组成:1S + 8E + 23M,双精度浮点数组成:1S + 11E + 52M.

这里写图片描述 单精度浮点数

这里写图片描述 双精度浮点数

  • 符号位(S):用于区分正数和负数,0 表示正数,1 表述负数。
  • 阶码(E):十进制数转换为二进制数后,向左移动小数点到第一个1之后,记移动位数为 x(负数表示向右移动的位数),记阶码中间数为 m(8位阶码的中间数为127,11位阶码的中间数为1023),令 e = x + m,则 e 的二进制码即为阶码 E。
  • 尾数(M):十进制数转换为二进制数后,向左(或向右)移动小数点到第一个1之后,则小数点之后的二进制码即为尾数 M(不包含第一个1)。

​ 十进制数 d 与编码后的二进制浮点数中 S、E、M 的函数关系如下:

img

​ 其中,M' 为 M 前面补上 “1.” 之后的二进制数,m 为阶码中间数的二进制编码,8位阶码的中间数为127,11位阶码的中间数为1023。

​ 例:123.456 的浮点数编码过程如下:

// 1.符号位
123.456为正数,符号位为0
S = 0

// 2.阶码
123.456的二进制码为:1111011.0111010010111100011010101
小数点移到第一个1之后,需要向左移动6位
6 + 127 = 133
133对应的二进制为:10000101
E = 10000101

// 3.尾数
1111011.0111010010111100011010101的小数点向左移动6位:1.1110110111010010111100011010101
去掉前面的"1.",剩下:1110110111010010111100011010101
M = 1110110111010010111100011010101

// 4.浮点数编码
S E        M
0 10000101 1110110111010010111100011010101
// System.out.println("0" + Integer.toBinaryString(Float.floatToIntBits(123.456f)));

4 最值问题

​ 对于单精度浮点数,最大值、最小值、最小正数分别为:

// 1.最大数
// System.out.println("0" + Integer.toBinaryString(Float.floatToIntBits(Float.MAX_VALUE)));
浮点数编码:0 11111110  11111111111111111111111
十进制数:1.9999998807907104 * (2 ^ 127) = 3.4028235E38

// 2.最小数
// System.out.println(Integer.toBinaryString(Float.floatToIntBits(-Float.MAX_VALUE)));
浮点数编码:1 11111111  11111111111111111111111
十进制数:-1.9999998807907104 * (2 ^ 127) = -3.4028235E38

// 3.最小正数
// System.out.println("0000000000000000000000000000000" + Integer.toBinaryString(Float.floatToIntBits(Float.MIN_VALUE)));
浮点数编码:0 00000000  00000000000000000000001
十进制数:1.0000001192092896 * (2 ^ (-127)) = 1.4E-45

​ 说明:“^” 表示幂运算。

5 精度问题

5.1 精度丢失原因

​ float 32 和 int 32 都是用 32 位二进制数表示,能表达的信息量应该一样多,即能映射的数字个数应该一样多。但是,float 32 能表示的最小值和最大值分别为 -3.4028235E38、3.4028235E38,int 32 能表示的最小值和最大值分别为 -2147483648、2147483647,float 32 的表示范围明显比 int 32 大,因此 float 32 必定会有精度损失,某些整数或小数不能映射到。

​ 对于单精度浮点数,令尾数 M 表示如下:

img

​ 令 M' = 1.M,即

img

​ 令 e = E - 127,则 e 表示 M' 的小数点需要向右(负数表示向左)移动的位数。

​ 下面将根据 e 的取值范围讨论单精度浮点数的精度问题,为简化研究,下文只讨论正数,即 S = 0 的情况。

(1)0 <= e <= 23

​ 在此范围内,[M' * 2 ^ e] 能表示的数可以用如下通式表示:

img

​ 如果 a23 之后添加 1,就会造成小数精度丢失。

(2)-127 <= e < 0

​ 在此范围内,[M' * 2 ^ e] 能表示的数可以用如下通式表示:

img

​ 如果 a23 之后添加 1,就会造成小数精度丢失。

(3)23 < e < 127

​ 在此范围内,[M' * 2 ^ e] 能表示的数可以用如下通式表示:

img

​ 如果将 a23 之后的某些 0 改为 1,就会造成整数精度丢失;如果在小数点后添加 1,就会造成小数精度丢失。

5.2 刻度与有效位

(1)1为刻度的有效范围

​ 通过第 2 节思考中案例 3,我们知道:16777216 + 1 不能进位到下一个整数,因此 1 不能作为 [-3.4028235E38, 3.4028235E38] 范围内整数的最小刻度,其有效范围是:[-16777216, 16777216]。

// 16777215 转换为二进制数:
System.out.println("0" + Integer.toBinaryString(Float.floatToIntBits(16777215f)));
浮点数编码:0 10010110 11111111111111111111111
二进制编码:111111111111111111111111 //24个1

// 16777216 转换为二进制数:
System.out.println("0" + Integer.toBinaryString(Float.floatToIntBits(16777216f)));
浮点数编码:0 10010111 00000000000000000000000
二进制编码:100000000000000000000000 //24个0

//16777216 的浮点数编码中尾数+1
浮点数编码:0 10010111 00000000000000000000001
二进制编码:1000000000000000000000010 //23个0
十进制数:16777218

​ 由上述分析知,16777217 无法使用 32 位浮点数表示出来,因此 16777216f + 1 后还是 16777216f,即出现思考中的整数精度丢失现象。

​ 同理,可求出双精度浮点数中,1 为刻度的整数有效范围是:[-2^53, 2^53],即 [-9007199254740992, 9007199254740992]

(2)十进制有效位数

​ 单精度浮点数,二进制有效位为 23 位,有效位数为 6.92 位:

img

​ 双精度浮点数,二进制有效位为 52 位,有效位数为 15.65 位:

img

​ 声明:本文转自浮点数编码原理

标签:编码,二进制,浮点数,System,println,原理,out
From: https://www.cnblogs.com/zhyan8/p/17232795.html

相关文章

  • 计算机网络原理 - 计算题汇总
    时延注意单位换算例题一例题二答:A-C发送过程无需排队,因此时间延迟就是三段链路发送延迟的和。A发送延迟=4/20=0.2s路由器1发送延迟=4/40=0.1s路由器2发送延迟=4......
  • Kubernetes应用访问层扩展CRD接入Prometheus原理
    1、kubernetes扩展点kubernetes应用访问层扩展点:webhook、operator、aggregator、容器探针、启停回调、kubectl插件。kubernetes基础设施层扩展点:调度器扩展、存储插件、网......
  • 数值积分原理与应用
    1理论计算定积分时,可以通过微元法逼近,如下:若取值均匀,公式如下:通过上述公式可以看到,定积分的值可以通过取样点函数值的线性组合得到。当取样点已经确定时,定积分的精度......
  • 【MyBatis框架】查询缓存-一级缓存原理
    查询缓存1.什么是查询缓存mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。mybaits提供一级缓存,和二级缓存。缓存模式图如图一级缓存是......
  • 给你清清楚楚讲明白HTTPS原理
    为什么用了HTTPS就是安全的?HTTPS的底层原理如何实现?用了HTTPS就一定安全吗?HTTPS的实现原理大家可能都听说过HTTPS协议之所以是安全的,是因为HTTPS协议会......
  • 贝塞尔曲线原理
    1前言贝塞尔曲线(Béziercurve)由法国数学家PierreBézier于1962年提出的一种矢量曲线,广泛应用于工程绘图、动画设计等领域。贝塞尔曲线是一种运动轨迹曲线,由n......
  • 【Java邮件开发】3.邮件协议总结与邮件服务器的工作原理
    我们来对邮件协议进行总结,并探讨邮件服务器的工作原理一、邮件协议剖析1.指令过程描述记得上一篇总结,我们手动敲指令发邮件的时候,登录smtp服务器的......
  • 转置原理
    基本原理我们知道矩阵的转置满足如下性质:记\(V=E_1E_2\dotsE_k\),则\(V^T=E_k^T\dotsE_2^TE_1^T\)。这也就相当于,如果我们有快速的方法得到出\(V^T=E_k^T\dotsE_2^......
  • Java synchronized的实现原理
    通常在多线程执行的过程中,我们需要考虑一些线程安全的问题,而线程安全问题中最常用的解决策略之一就是“锁”。加锁的本质,就是为了解决在多线程场景中对于共享数据访问的......
  • 光场原理及一些算法代码实现
    2023.3.18好久没有写过博客了,感觉自己比以前更菜了\(//∇//)\好不容易的更新,是为了把最近看的几篇光场论文写个自己的整理和理解,后面可能会写一些用C++实现的光场处理算......