第一次a 0.1 + 0.2 === 0.3 bug 在制作中咬了我,我做了大多数开发人员所做的事情:我搜索,找到"浮点很奇怪,"然后继续前进,多年后我才真正看了这些位并理解了为什么,一旦我这样做了,一整类数字 bug 就不再神秘了 本指南将浏览 IEEE 754 转换器向您展示的内容,使用 IEEE 754 转换器 toolz。dev上,以及为什么盯着符号,指数和尾数是建立计算机如何存储小数的真实直觉的最快方法。
TL;博士: IEEE 754标准,计算机将浮点数存储为符号、二次方和分数有效数,IEEE 754转换器将像3.14159这样的十进制编码为那些精确位,并将位解码回一个值 IEEE 754 转换器 对于单精度和双精度,完全在您的浏览器中执行,符号、指数和尾数都会爆发。
什么是 IEEE 754?
IEEE 754 是该标准,于 1985 年首次发布并修订为 IEEE 754-2019的,那就定义了计算机如何用二进制表示浮点数,几乎每种CPU、GPU、编程语言都遵循它,这就是为什么a double c、java、python、javascript中表现得一样,当人们说"浮点,"时,他们几乎总是表示ieee 754二进制浮点。
核心思想是数字分三部分存储,有一个符号位表示正或负,有一个指数将值缩放二次方,并且有一个尾数,也称为分数或有效数,将精确的数字放在一起,一个正常数字是符号乘以1.尾数乘以2到指数,它是科学的符号,以二为底,打包成固定数量的位。
2种格式你每天遇到的就是单精度,叫做binary32,双精度,叫做binary64,单使用1个符号位,8个指数位,23个尾数位,总共32位,双使用1个符号位,11个指数位,52个尾数位,总共64位,双是大多数语言默认的浮点类型;单常见于GPU和内存敏感代码中。
IEEE 754转换器实际上是做什么的?
IEEE 754转换器取一个数字,并向您显示其精确的位模式,将其分割成这三个字段,以及十六进制形式和从位读回的值。它双向工作:输入十进制对其进行编码,或粘贴二进制或十六进制位模式将其解码回其所代表的数字。
Toolz。dev 上工作流程短 选择单精度还是双精度 选择您的输入是十进制数、十六进制位模式还是二进制位模式 输入值 该工具显示符号位、指数字段、尾数、全位、十六进制、有偏和无偏指数以及精确存储值 每个字段都有一个复制按钮。
查看所有这些,而不是相信您的语言打印的数字,原因是打印的数字是遗漏的。当您的控制台显示时 0.1,它正在向你展示一个圆润、友好的渲染,这些位告诉你真正存储的内容,而这两者的区别在于浮点错误的生存地点。
0.1加0.2为什么不等于0.3?
这是规范的例子,转换器使其具体化。值 0.1 不能完全用二进制浮点表示,出于同样的原因,1/3 不能完全用十进制写成。在以十为基数的情况下,1/3 是 0.3333。。。在以二为基数的情况下,0.1 是一个永不终止的重复分数,因此必须将其四舍五入才能拟合 52 个尾数位。
0.1的双倍解码,位实际持有的值是0.1000000000000000055511151231257827021181583404541015625,0.2的双倍同样是头发太大,加上两个圆角值,结果比0.3多了一点点,但0.3的双倍少了一点点,所以两者没有被打破,每一步都完全按照IEEE 754的要求去做,转换器让你看着它发生,而不是相信它。
一旦你看到了这一点,修复程序自然会出现:永远不要比较浮点数来获得精确的相等性。在小公差内进行比较,或者使用整数美分来获取金钱,或者在需要精确的十进制算术时使用十进制库。该错误从来不在加法中。它期望二进制格式能够精确存储十进制分数。
符号、指数和尾数是什么?
每个字段都有特定的作业,转换器标记所有三个字段。
號(sign bit)最簡單:0表示正,1表示負,因為號是分開的位,IEEE 754同時有正負零,在比較中表現相等,但帶有不同的位。
指数字段存储的幂为 2,但添加了偏差,因此它可以表示负指数,没有单独的符号。单精度偏差为 127,双精度偏差为 1023。因此,单精度存储的指数字段为 127 表示实际指数为零。toolz。dev 转换器显示两个数字:存在于位中的偏差值,以及实际应用的二的无偏差幂。这种区别会使很多人跳动,这就是为什么该工具将其拼写出来,而不是让你在头脑中减去。
数(mantissa)持有有效数的小数部分,对于一个正常数,有一个隐含的前导1没有被存储,因为一个归一化的二进制有效数总是以1开头,所以格式通过省略它来获得自由位精度,这就是为什么单精度给出大约7个十进制数字,双给出大约15到16,即使尾数域是23和52位。
以下是两种格式如何逐字排列:
| 财产 | 单(二进制32) | 双(二进制64) |
|---|---|---|
| 总位 | 32绔 涔诲芥澶澧 | 64绔 涔诲芥澶澧 |
| 符号位 | 1 | 1 |
| 指数位 | 8 | 11绔 涔诲芥澶澧 |
| 尾数位 | 23绔 涔诲芥澶澧 | 52绔 涔诲芥澶澧 |
| 指数偏差 | 127绔 涔诲芥澶澧 | 1023 |
| 大约十进制数字 | 7 | 15 至 16 |
| 俗名 | 浮 | 双倍 |
如何将位转换回十进制数?
Decoding和编码一样有用,它就是你从存储器转储、二进制文件格式或存储原始浮动的网络协议中读取一个值的方式。将输入类型设置为Hex或Binary,并粘贴精度期望的精确位模式:8个十六进制数字或32位单位,16个十六进制数字或64位双位。
例如,单精度六角形 40490FDB 3.1415927解码,最接近圆周率的浮点数,转换器也接受一个可选 0x prefix,并忽略空格和下划线,因此您可以随便找到它们粘贴分组字节,如果给它错误的位数,它会准确地告诉您精度需要多少而不是默默地截断,这是我希望更多工具费心编写的错误消息。
该解码方向自然与一般方向配对 数值基数转换器 当您需要在十进制、十六进制、二进制和八进制之间移动一个值时,无需浮点解释,并且使用 二进制翻译 当你使用文本而不是数字时。我经常够了所有三个,以至于它们都住在我书签的同一个角落里。
无穷大、NaN 和零是如何表示的?
IEEE 754变得聪明的地方是特殊的值,转换器为你识别每种情况,这样你就不必记住这些模式。
指数场充当开关,当每个指数位都是 1 时,您处于特殊范围:全零尾数表示无穷大,任何非零尾数表示 NaN,而不是数字。正无穷大和负无穷大仅因符号位而异。 NaN 是您从零除以零或负数的平方根等操作中获得的,并且它具有其自身不相等的著名属性。
0时,你在另一个特殊范围:一个全零尾数为零,正或负取决于符号位,一个非零尾数是一个次正规数,次正规数填补了零和最小正常数之间的空白,交易精度表示非常微小的量值的能力,他们使用一个减去偏差的有效指数,他们下降隐含前导1,当转换器显示无偏指数时,它占了。
Everything else,其中指数域既不是所有零,也不是所有一,都是一个正常的数字,能够看一眼位模式,瞬间知道你正在看这五种情况中哪一种是真正的调试超级大国,当一个计算产生一个你没有预料到的值。
我什么时候真正需要这个?
您需要它的频率比您知道查看后猜测的要多。当数字结果因舍入错误而关闭时,我会伸手去拿它,我想确认该值是否可以精确表示。我在编写或调试二进制序列化器、文件格式或存储浮动的线协议时使用它,因此我可以验证字节与我的意图相匹配。在比较单双精度时,例如决定 GPU 着色器是否可以使用,这是必不可少的 float bu失太多准确性。而且是我所知道的计算机体系结构最清晰的教具之一, 因为看到字段使抽象的标准有形。
如果你按照我的方式在堆栈中构建,浮点会出现在令人惊讶的地方:Laravel API 中的价格计算、React 中的画布动画、破坏坐标的 CSV 导出。了解存储格式是一项小投资,可以在所有这些领域得到回报。我写过这样的工具如何融入更广泛的套件中 web开发人员工具包并且 数基转换器指南 並 二进制翻译指南 更深入地覆盖邻近的低级转换。
浮点精度的极限是什么?
一旦你能看到这些位,格式的限制就不再是抽象的了。双数有 52 个存储的尾数位加上隐含的前导 1,它大约可以得到 15 到 16 位有效十进制数字。推过那个,精度悄然蒸发。双数可以表示的最大整数,其中仍然精确的每个值是 2 的 53 次方,即 9007199254740992。向后添加一个结果,然后将结果向下舍入,因为下一个可表示值是两个远的,而不是一个。这就是为什么对所有数字使用双数的语言(其中包括 JavaScript)暴露了 a Number.MAX_SAFE_INTEGER 精确地保持该值,以及为什么作为 JSON 编号发送的大型数据库 ID 会默默损坏。
1个可表示值与下一个可表示值之间的差距,叫做最后一个地方的单位,或ULP,并且随着幅度的增长而增长,在1.0附近差距很小;在十亿附近是几百个;在最大双倍附近,大约1.8乘以10到308,邻居之间的差距在天文上很大,转换器使这一点可见:编码两个附近的大数字,你经常会发现它们共享相同的位,因为它们之间根本没有表示理解ULP是阻止你期望比格式所能给出的分辨率更多的原因。
一个相关的陷阱是灾难性的消除,当你减去两个几乎相等的浮点数时,前导数字会消除,而你只剩下低阶位,这是最不准确的部分。即使每个输入看起来都很精确,结果也可以以舍入误差为主。这就是为什么在数字上仔细地重新排序操作以避免减去接近的数量,以及为什么在朴素循环中对一长串浮点数求和会累积像卡汉求和这样更好的算法避免的误差。
实用外卖是一致的。不要将钱存储为浮动;使用整数美分或十进制类型,因为像 0.10 这样的值无法精确表示,并且错误会在数千笔交易中复合。不要将浮动与精确相等进行比较;在与您的问题大小的公差范围内进行比较。当一个非常大的整数必须在往返中生存时,将其保留为字符串或使用任意精度的整数类型,而不是相信双数来持有它。一旦转换器向您展示了格式为何如此行事,这些规则中的每一个都更容易记住。
常见问题
如何将十进制数转换为 IEEE 754?
选择单精度或双精度,将输入类型保留在十进制上,键入数字并进行转换。该工具将该值编码为其 IEEE 754 位模式,并显示符号位、指数字段、尾数、全二进制和十六进制。它全部在您的浏览器中运行。
单精度和双精度有什么区别?
单精度(binary32)使用32位:1个符号、8个指数和23个尾数,给出大约7位十进制数字的精度。双精度(binary64)使用64位:1个符号、11个指数和52个尾数,给出大约15到16位。双是大多数语言中默认的浮点类型;单在 GPU 和内存受限代码中很常见。
0.1为什么不转换为精确值?
0.1在二进制浮点中没有有限表示,就像1/3没有有限小数一样,最接近的可表示双是0.1000000000000000055511151231257827021181583404541015625,转换器显示从位读回的存储值,因此可以看到像0.1 + 0.2这样的微小差异不等于0.3。
符号、指数和尾数是什么?
0为正数,1为负数,指数域存储加偏差的二次方(单127,双1023),这就是为什么工具同时显示位中的偏值和它所应用的无偏幂尾数持有有效数的分数部分,对于正常数则隐含前导1。
IEEE 754 位可以转换回十进制数吗?
是将输入类型设置为 Hex 或 Binary 并粘贴精确的位模式:8 个十六进制数字或 32 位用于单精度,16 个十六进制数字或 64 位用于双精度。工具对其进行解码,并显示其所表示的十进制值以及完整的字段细分。
无穷大、NaN 和零是如何表示的?
1时,全零尾数表示无穷大,非零尾数表示NaN,当每一位指数位为0时,全零尾数为零(正或负取决于符号位),非零尾数为次正规数,转换器为您标记这些情况中的每一个。
对于敏感号码使用此工具安全吗?
是转换完全在浏览器中的JavaScript中运行,没有网络请求携带您的输入,没有存储任何内容,并且一旦页面加载,该工具就会离线工作,因此您输入的任何值都会保留在您的设备上。
转换器期望什么六角形格式?
16进制纯数字,可选0x前缀和空格或下划线进行分组,忽略不计,单精度期望8个十六进制数字,双精度期望16,例如40490FDB为单精度pi(3.1415927)。
免费探索自己的部分 IEEE 754 转换器。它完全在浏览器中对单双精度浮动进行编码和解码,上传任何内容。



