Skip to content

Latest commit

 

History

History
287 lines (212 loc) · 8.45 KB

172-532046-浮点运算误差.sy.md

File metadata and controls

287 lines (212 loc) · 8.45 KB
show version enable_checker
step
1.0
true

浮点运算误差

回忆

  • 浮点数有三个部分组成
    • 符号位
      • 负责正负
      • $0$ 就是正
      • $1$ 就是负
    • 指数部分
      • 负责翻倍
      • $01111111$ 为平衡位置
      • $+1$ 相当于乘以 $2$
      • $-1$ 相当于除以 $2$
    • 尾数部分
      • 前面有一个 $1$ 不显示
      • 默认就是 $1.0$
      • 第一位写一个 $1$ 代表 $1.1$
      • 第二位再写一个 $1$ 代表 $1.11$
  • 那我们回来再看看 $0.1$
    • 有的时候也会出现无法理解的情况

图片描述

  • 这出现了误差?!

0.1

  • 3 个 $0.1$ 相加等于 $0.30000000000000004$
  • 观察 $0.1$

图片描述

图片描述

  • 这个算出来
  • 确实比 $0.1$
  • 加在一起确实超过 $0.3$🤪

双精度

  • 我们前面介绍的都是单精度浮点型 float
    • 单精度浮点型 float 总共 4 字节
  • 还有一种双精度浮点型 double
    • 总共 8 字节

图片描述

图片描述

  • 那为什么加起来是 $0.30000000000000004$ 呢?
  • 我们先来看看和的双精度数的二进制形态

和的形态

  • $0.1+0.1+0.1$ 的双精度形态
  • \x34\x33\x33\x33\x33\x33\xd3\x3f

图片描述

  • \x34\x33\x33\x33\x33\x33\xd3\x3f 对应十进制
  • $0.30000000000000004$
  • 前后都能说通
  • 可是,这个和 $0.1$ 到底是怎么加的呢?

计算

图片描述

  • 首先获得 $0.1$ 的双精度浮点型的二进制形态
  • 刨去前面的符号位和指数部分
  • 还剩下总共 52 位 2 进制数
    • 如上图选中的数字部分
  • $1001100110011001100110011001100110011001100110011010$
    • (可复制到剪切板)

补位

  • 分成两部分

    • 有效数字
    • 指数
  • 有效数字

    • 前面有个缺省的 1
    • 把这 52 位 2 进制数前面补 1
    • 并加上小数点
      • $1.1001100110011001100110011001100110011001100110011010$

图片描述

  • 指数部分
    • -4
  • 综合起来就是
    • $1.1001100110011001100110011001100110011001100110011010$^2-4

运算

  • 乘以 3 得到有效数字
    • 此时的数值是原来的3倍
    • 大致是0.3
    • 数量级和原来差22

图片描述

  • ieee-754要求第1位必须有个缺省的1

    • 所以小数点要向左移动2位
    • 由于是2进制数
    • 小数点向左移动2位就相当于要除以 4
  • 综合起来就是要对有效数字

    • 先✖3
    • 再÷4

运算

  • 先算有效数字
    • 先粘贴有效数字部分
    • 再✖3÷4
    • 结果向上(ceil)取整
    • 并转化为2进制(binary)
    • 有效数字是(1.)0011001100110011001100110011001100110011001100110100
    • 去掉红框中的1
    • 保留后面的有效数字
    • 然后转化为 16 进制
      • $0x3333333333334$
    • 依然是 52 位 2 进制数
    • 共 13 位 16 进制数

图片描述

  • 指数部分
    • 从3FB变成3FD
  • 整体是
    • 0x3FD3333333333334
    • 00111111 11010011 00110011 00110011 00110011 00110011 00110011 00110100
    • 可复制出来去binaryconvert.com验证

图片描述

验证

图片描述

  • 通过系统中的字节状态进行验证
  • $0.1+0.1+0.1$ 得到的 $0.30000000000000004$
  • 对比直接进行的二进制编码

图片描述

  • 这是一致的
  • 我可以手算这个过程么?
  • 说干就干,走起来~

手算

图片描述

  • 把后 52 位复制出来

计算过程

图片描述

  • 第一步是相加
  • 两个一样的数,左移 1 位
  • 然后再加上自身
  • 得到结果是尾数
  • 尾数以 $1.xxx$ 开头
  • 向右移动 2 位
  • 然后掐头去掉开头默认的 1
  • 结尾多出的两位有进位
  • 保持 52 位

浮点运算进化

  • 早年间其实 Guido 的 python 是这样的
    • 整型变量可以和整型变量运算
    • 浮点型变量可以和浮点型变量运算
    • 整型和浮点型之间不能加减乘除
    • 为什么呢?

图片描述

原因

  • 指令集不同
    • 整型数字加法有自己的指令集
    • 浮点型数字加法有自己的指令集
    • 没有一个指令能算整型加上浮点型
    • 要在系统里把整型转化为浮点型
    • 再用浮点型指令集来做
  • 早年间甚至有专门负责浮点运算的协处理器
  • 想象一下 $1e100 + 1e-100$
    • 有效数字需要对齐

图片描述

解决

  • 这个东西虽然有了指令
  • 但是还是比较复杂的
  • 解决这个问题的是 Tim Peters,美国的一位软件工程师
  • 他在 Python 发展的早期,就参与了这个语言的开发和设计。

图片描述

  • 他原来在为大型主机写编译器
  • 后来业余时间加入了 python 这个项目
  • 他是 Python 语言以及 CPython 的主要 contributor 之一
  • 他创造了 Timsort hybrid sorting 算法
  • 这个算法从 Python2.3 开始就在使用
  • 很硬核
  • 原来的整型和浮点型不能相加减
  • 他让两个不同类型的数字可以相加
  • Python 标准模块中的 doctest 和 timeit,也是他的大作
  • 他也是 python 之禅的作者

python 之禅

  • 2001 年秋,Foretec(一家会议组织公司)正在准备召开第十届 International Python Conference(IPC 10,Pycon 的前身)
  • Foretec 打算征集一条印在会议 T 恤衫上的标语,最终他们从 Python 社区收到了 500 多条投稿
  • Foretec 邀请了 Python 的核心开发 Guido,Fred,Jeremy,Tim Peters,Barry 等来担任评审
  • 评审委员们从投稿中过滤出了 130 多条后就一直没有进展,直到会议快要召开时他们也没有确定最终标语
  • 在会议就要开始的前几天,到了评审们不得不做出决定的时候了
  • 于是由 Tim Peters 和 Barry 两人轮流评审,每人每次淘汰一半留下一半,直到最后只剩一条
  • 最终,“import this”被选了出来,大家对“import this”非常满意
  • 但是当选择了这条以后,他们意识到他们必须要实现它
  • 经过简单的讨论后,“import this”的被定为输出 Tim Peters 写的《The Zen of Python》
  • ——Python 编程和设计的指导原则。
  • this.py 在 lib 里面是标准库
  • 去找一下

查找

  • sudo find / -name "this.py"

图片描述

  • 这是啥?

解码

  • 代码中的 s 字符串使用了 rot13 加密
  • rot13 就是凯撒密码的一种,每个字母变换为它后面的第 13 个字母

图片描述

  • 等到 IPC 10 结束后
  • 他们悄悄的把代码提交到 Python 2.2.1
  • 过了一段时间,才逐渐有人发现“import this”这个彩蛋

回头看

图片描述

  • 我们现在回头再看这个东西

图片描述

  • 能找到原因了吗?

总结

  • 这次了解浮点类型变量
  • 有两种表示法
    • float 4 字节 单精度浮点型
    • double 8 字节 双精度浮点型
  • 浮点型特点
    • 第一位都是符号
    • 小数点都浮动
    • 浮动程度由指数部分决定
    • 尾数部分默认 1 开头
    • 后面的小数部分是有效数字
  • 这就是浮点类型的数据
  • 不过很容易出错
    • 这个问题简直致命!
    • 太容易出错
    • 尤其是和钱相关的
    • 有没有减少出错的方法?
    • 最好是十进制理解下的完全正确?🤪
  • 下次再说 👋