Minitest assert_equal断言失败咨询:数值看似相等却不匹配
嘿,这个问题我之前也碰到过!本质上是浮点数的二进制存储特性和Minitest assert_equal 的严格比较逻辑在搞鬼,咱们一步步说清楚:
为啥明明数值一样却断言失败?
你看到的 -0.904e1 其实就是 -9.04 的科学计数法写法,数值上完全等价,但Minitest的 assert_equal 对浮点数是严格的数值相等校验——而计算机里的浮点数是用二进制存储的,很多十进制小数根本没法被精确表示。哪怕你调用了 round(2),最终得到的浮点数在内存里的二进制表示,可能和你直接写的 -9.04 字面量存在极其细微的差异。
举个实际的例子:假设 elts[2].change 的原始值是 -9.040000000000001,round(2) 之后打印出来是 -9.04,但它的底层存储和你写的 -9.04 字面量并不完全一样;反过来如果原始值是 -9.039999999999999,round(2) 后显示也是 -9.04,但同样和字面量的存储有差别。这种细微差异就会让 assert_equal 判定两者不相等。
至于科学计数法的显示,只是Ruby输出浮点数的一种格式而已,本质还是同一个数值范围,核心问题还是精度。
怎么解决?
别用 assert_equal 来比较浮点数!Minitest专门提供了 assert_in_delta 方法,它允许你设置一个可接受的误差范围,完美避开浮点数精度的坑:
# 允许±0.001的误差,完全覆盖两位小数的精度需求 assert_in_delta -9.04, elts[2].change.round(2), 0.001
如果你的业务场景要求绝对精确的十进制运算(比如财务相关),那更推荐用Ruby的 BigDecimal 类型来处理,从根源上避免浮点数精度问题:
require 'bigdecimal' expected = BigDecimal("-9.04") actual = BigDecimal(elts[2].change.to_s).round(2) assert_equal expected, actual
为啥其他测试能过?
运气好而已😂 其他测试的数值刚好能被二进制精确表示,或者它们的精度差异小到没触发这个问题。而这个测试的数值刚好踩中了浮点数的精度盲区,导致存储表示和字面量不一致,才暴露了问题。
内容的提问来源于stack exchange,提问作者Andrew Cetinic

