IEEE 754规范: 五, 一些补充

本章讨论一些零碎的话题.

一. 其他导致浮点数存储不精确的原因

在第三章中我们提到过, 数学中的小数是连续的, 而计算机中的小数(准确来说是ieee754标准中的小数)是离散的.

这就会引发精度问题: 图中的绿色指针只能指向蓝点, 不能指向蓝点之间的数. 比如上面最右边的图, 绿色指针其实无法指向0.3, 当你想要指向0.3时, 实际上会被舍入为0.234, 即舍入到离它最近的蓝点对应的值.


而除此之外, 进制问题也会导致IEEE754浮点数存储不精确

简单来说就是: 有限长度编码下, 每种进制都有他们不能精确表示的值

比如: 10进制不能精确的表示1/3 (0.3333333.....)

十进制可以精确表示1/5 (0.2), 但二进制无法精确表示1/5

不能精确表示时,只能进行近似. 编码长度越长,近似程度越高

举例: 下图尝试用二进制表示0.2, 可以发现, 只能近似表示...

当你把一个十进制数存储到计算机中时, 实际上存储的是该数的二进制表示.

所以, 当你写入如下代码时:

float f = 0.2;

虽然0.2(十进制)远远没有到达32位浮点数的精度上限(7位精度), 但计算机其实无法精确地存储该数值, 因为0.2(十进制)无法使用二进制格式精确表示.

此时变量f对应的内存状态是这样的:

↑ 你键入的是0.2

↑ 内存中实际存储的是0.20000000298023223876953125

可以在c语言中验证一下:

可见十进制的0.2无法用二进制精确表示, 但十进制的0.5却可以用二进制精确表示.

二. 二进制的小数形式

有些同学可能会纳闷, 二进制为什么会有小数形式? 我常见的二进制都是整数形式啊, 比如十进制的9, 表示为二进制是1001, 怎么会有 1001.101 这种二进制的小数格式呢.

其实对于程序员来说, 这里确实比如容易让人困惑, 比如win10自带的计算机, 就不支持二进制小数:

二进制模式下, 小数点是不能用的

许多编程语言, 比如js, 也不支持直接使用二进制小数:

支持二进制整数, 但不支持二进制小数

但和十进制一样, 二进制其实也有小数形式, 而且很容易理解:

比如对于十进制数 78.23

十位: 7, 表示 7 * 10^1 = 70

个位: 8, 表示 8 * 10^0 = 8

十分位: 2, 表示2/10, 或说表示2 * 10^{-1}

百分位: 3, 表示3/100, 或说表示3 * 10^{-2}

这个十进制所表示的值是: 70 + 8 + 2/10 + 3/100


二进制数也是同理的:

比如对于二进制数 10.11

第一位: 1, 表示1 * 2^1 = 2

第二位: 0, 表示0 * 2^0 = 0

第三位: 1, 表示1 * 2^-1 = 0.5

第四位: 1, 表示1 * 2^-2 = 0.25

所以这个二进制表示的值, 其实就是十进制的2 + 0 + 0.5 + 0.25 = 2.75


这里比较有意思的一点是:

十进制小数点后面的那一位(也就是十分位), 对应的是1/10, 也就是0.1

即, 对于十进制数3.4, 这个4对应的值是: 4 * 权  =  4 * 0.1 = 0.4

而二进制小数点后面的一位, 对应的是1/2, 也就是十进制的0.5

所以对于二进制数0.1, 这个1对应的值是: 1 * 0.5 = 0.5, 所以二进制的0.1, 其实等于十进制的0.5

这让我想起来一个脑筋急转弯, 问: 什么时候 1.11.3 要大?

答: 当1.1是个二进制数, 而1.3是个十进制数的时候...


事实上: 对于小数点之后的位, 二进制的位权始终比十进制的位权要大, 举例:

十进制数: 小数点之后的位权依次是: 1/10,  1/100,  1/1000...

二进制数: 小数点之后的位权依次是: 1/2,  1/4,  1/8...  相应位的权始终比↑十进制的要大

所以会出现这种现象

二进制:                1.000001, 小数点后面的数看起来已经很小很小了

对应的十进制是:  1.015625, 小数点后面的数其实还挺大...


在IEEE765标准中, 我们会经常和二进制小数打交道, 所以这里补充一下相关知识.


三. 关于32位浮点数, 一些不太正确的认知

1. 32位浮点数能存储很大的整数

这是32位浮点数的取值范围:

[-3.4*10^{38}, -1.18*10^{-38}] ∪ [1.18*10^{-38}, 3.4 * 10^{38}]

当我第一次看到这个取值范围时, 我是很惊讶的, 怎么这么大?

一个浮点数, 占用32字节, 竟然能存储下约±340000000000000000000000000000000000000这么大的数

相比之下, 一个同样32字节的long类型, 存储范围只有约±2147483647

那我为啥还要用long类型...

...

一路学习到现在, 倒是可以绕过这个弯儿了, 那就是:

32位浮点型确实最大可以存储到3.4 * 10^{38}这么大的数, 但精度很低

第三章中我们说过, 32位浮点数表盘中的蓝点会越来越稀疏:


等到了3.4 * 10^{38}这么大的数时, 其实蓝点已经稀疏的不成样子了, 基本是不可用状态

根据wiki中给出的间隔, 对于1.70141e38 到 3.40282e38范围中的数, 间隔是2.02824e31

也就是说, 大体上: 32位浮点数中, 能精确存储1.70141e38

但无法精确存储1.70141e38 + 1

也无法精确存储1.70141e38 + 2,

也无法精确存储1.70141e38 + 100000000000

...

下一个能精确存储的数是: 1.70141e38 + 20282400000000000000000000000000 (即加上间隔)

这个精度基本上是不可用的.

事实上, 如果你要用float存储整数的话, 最多只能精确存储到 16777216

再大的话, 间隔就会变为2, 就不适合用来存储整数了:

此时再回过头来看看同为 32位 的long类型, 能精确存储的整数范围

约是: ±2147483647

比:    ±16777216    大多了

所以存储大整数还是用long类型吧

总结: 32位浮点数只是有能力存储到3.4 * 10^{38}, 实际上存储的数过大会导致精度过低, 基本上不可用. 用32位浮点数存储整数时, 只适用存储±16777216之间的整数.


2. 32位浮点数能存储很精确的小数

这是32位浮点数的取值范围:

[-3.4*10^{38}, -1.18*10^{-38}] ∪ [1.18*10^{-38}, 3.4 * 10^{38}]

看起来好像能存储1.18*10^{-38}这么精确的小数...

但其实和存储整数一样, 32位浮点数只是有能力存储到1.18*10^{-38}这么小的小数而已...

事实上在第三章中我们详细讲解过: 32位浮点数的精确度是7位有效数.

即如果你要存储的数 整数部分 + 小数部分 放在一起超过了 7 位, 32位浮点数就不能精确存储了

比如, 32位浮点数就不能精确存储我们常背的部分圆周率

32位浮点数倒是可以存储常见的月工资, 比如 5078.65, 或 12665.73. 但如果要存储年工资, 或把工资存储到3位小数, 32位浮点数就不一定够用了...

所以, 虽然32位浮点数的取值范围看起来很大, 足足有:

[-3.4*10^{38}, -1.18*10^{-38}] ∪ [1.18*10^{-38}, 3.4 * 10^{38}]

但其实32位浮点数只适合存储常见数据...

感性地去认知的话, float(也就是32位浮点数)类型其实和int类型有些相似: int用于存储最常用, 最自然的整数. float则用于存储最常用, 最自然的浮点数...编程时, 如果要存储的数很大或精度很高(相对来说,这些数往往不怎么常用或不怎么自然), 就要考虑改用long或double.

精确来说的话, 就是不要被32位浮点数骇人的取值范围吓到. 而是记住事实上它只能存储7位有效数就行了.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352