指南为什么浮点转换器会失败(以及十进制数学如何修复它)
为什么浮点转换器会失败(以及十进制数学如何修复它)
了解为什么二进制浮点会产生奇怪的转换残差,当这些错误很重要时,以及十进制或有理算术如何保持单位转换器的可信度。
经典的 0.1 + 0.2 问题遇到单位
大多数 CPU 以二进制浮点数 (IEEE 754) 存储非整数。在十进制中看起来很简单的分数(0.1、0.001、2.54)通常无法用二进制精确表示。存储的值是最接近的可表示的近似值,并且近似值的算术会累积微小的残差。
单位转换重复乘除这些值。将英寸与毫米相互转换,或将升与立方米相互转换,您可能会看到 2.9999999997,而不是 3。物理转换系数是准确的;计算机表示则不然。
对于许多显示器来说,四舍五入后差异是不可见的。对于与财务相关的剂量、数控刀具路径、合法土地面积或期望完全相等的自动测试断言,这些残差会成为真正的错误。
哪些换算系数在纸面上是准确的
国际英寸正好是25.4毫米。国际英尺正好是 0.3048 m。一升正好是 0.001 立方米。在通常的实验室意义上,一立方厘米正好是 1 毫升(1 立方厘米 = 10⁻⁶ 立方米)。这些是定义,而不是测量。
如果软件将 25.4 存储为二进制浮点数,则在任何转换运行之前可能已经出现轻微错误。乘以另一个不精确的因子会加剧误差。即使算法在概念上是正确的,断言严格相等的往返测试也会失败。
温度添加偏移量:摄氏度 ↔ 开尔文使用 273.15。该常量也是二进制浮点数的近似十进制值。仿射转换特别敏感,因为添加不精确的偏移量会改变每个结果。
实践中出现浮点错误的地方
UI 转换器通常通过四舍五入到固定的小数位数来隐藏问题。用户看到干净的结果而不会注意到内部噪音。当另一个系统使用原始浮点数时就会出现问题:CAD 内核、CSV 导出或比较全精度字符串的单元测试。
连锁转换会加剧麻烦。每一步都带有二进制浮点数的英里→码→英尺→英寸→毫米→米比具有高精度系数的单英里→米转换要差。每跳都会重新舍入中间值。
数量的平方和立方会使错误变得更严重。面积和体积会乘以长度比例,因此当您展开 (1+ε)ⁿ 时,长度上的微小相对误差相对于面积或体积而言会变得大两倍或三倍。
小数、有理数和定点替代方案
小数库(例如,decimal.js、Big.js 或语言本机小数)以 10 为基数存储数字。精确的小数因数(例如 0.3048 和 0.001)在所选精度范围内保持精确。关心正确性的单位转换器通常通过十进制基本单位进行转换。
有理算术将因子存储为整数的分数。如果 1 英寸 = 127/5000 米(因为 25.4 毫米 = 127/5000 米),乘法可以完全抵消。有理数对于纯尺度转换来说是理想的;偏移仍然需要仔细处理温度。
定点数学将所有内容缩放到最小刻度的整数计数(纳米、微升或千分之一英寸)。整数运算在溢出之前都是精确的。它适用于具有明确测量量的领域。
值得信赖的转换器的实用规则
将精确的法律因子存储为小数或有理数,而不是仓促键入的二进制浮点数。以尽可能少的步骤转换输入 → 基础 → 输出。仅在针对人类进行格式化时进行舍入,并记录舍入模式。
切勿在二进制浮点数上测试严格相等的转换器。在绝对或相对容差范围内进行比较,或者以规定的精度比较十进制字符串形式。首选使用已知精确对的黄金测试,例如 1 英寸 = 25.4 毫米。
如果您构建一个计算器 UI,请显示足够的精度以供使用,但不要显示过多的数字,以免浮动噪声看起来像信号。通过浮点数转换后显示十二位小数的长度工具正在宣传其表示错误。
当二进制浮点仍然没问题时
粗略比较、游戏、数据可视化和许多科学模拟都可以容忍浮动误差,因为模型的不确定性使表示误差相形见绌。关键是了解你的耐受力。如果一毫米很重要,那么浮标就需要纪律或更换。
硬件加速和 GPU 管道通常会强制浮动。在这些情况下,请在 CPU 或服务器上保留十进制的权威转换,并将 GPU 浮点视为显示或中间近似值。
好的工程是将数字表示与您将用数字做出的决定相匹配。制造、医学和法定计量的单位转换需要小数或理性数学;天气小部件不会转换城市温度。如有疑问,请选择更严格的表示法 - 小数的性能成本通常小于静默往返错误的成本。
相关换算器
常见问题
为什么来回转换会改变我的号码?
二进制浮点数不能准确地表示许多小数因数。每次乘法或除法都会增加微小的舍入误差。即使转换因子在概念上是准确的,通过浮点的往返转换通常也无法恢复原始位。
双精度足以进行单位转换吗?
双精度 (float64) 足以满足显示舍入后的许多日常转换,但当您需要位稳定的往返、法定计量或严格的自动化测试时,它并不能替代精确的小数因数。对于权威转换器,更喜欢小数或有理数。
我应该如何比较测试中的转换器结果?
以声明的精度进行比较,使用十进制字符串相等,或断言绝对和相对容差。避免在几个算术步骤之后期望精确的二进制相等。