最近用单精度运算处理一段大数据,无意间发现了一个奇葩的 bug: x = rand('single') y = x; z = x; x(1) = x(1) + eps('single') y = y + eps('single') z(end) = z(end) + eps(single(1.0)) [isequal(x,y), isequal(y,z)] 如果没有 bug 的话,将得到 x、y、z 完全相等(这可以在双精度运算下得到验证,只需将上面代码里所有 single 换成 double 即可),但最终的结果却是 x 不等于 y,y 和 z 相等。之所以说这个 bug 有点奇葩,主要是因为以下几个现象有些奇怪: 1:多次运行上述代码,不论每次产生的初值 x 如何,最终得到的 x 只能取几个特定的值,如 1.1921e-07、2、-2、3.6893e+19、 -3.6893e+19 (对!连负数都出来了!) 2:x 与 y 的唯一区别在于,x 使用了数组索引而 y 没有,采用了完全相同的运算,然而不带索引的 y 的结果无误,而带索引的 x 明显错误 (索引还能造成差别?) 3:上面看到带索引会出错,可是 z 明明也带了索引(索引换成1或者end其实无所谓),可是 z 的结果却偏偏无误,而这里唯一的不同的是将eps('single') 换成了 eps(single(1.0)) ,可是 help 文档里清清楚楚地记载: eps('single') is equivalent to eps(single(1.0)) ,可他们造成的结果却截然不同! 如果这还不算奇怪,那我们继续往下看: 4:随便在上述代码里加一个断点后运行,x、y、z 的结果都准确无误了 (断点也能make a difference了?) 5:如果在程序里关掉 jit 加速器后,x、y、z 的结果也都准确无误了 通过上面的测试(以及一些额外的测试),似乎可以断定,只要代码里使用的是 eps(single(x)) (如果 x 原本不是single类型)或者 eps(x) (如果 x 已经是single类型),则不会有问题;但一旦代码里写的是 eps('single') 的形式,则很可能会出现一些奇怪的现象。所以,建议在新版本修正这一 bug 之前,尽量不要使用 eps('single') 的写法 。 最后交代一下测试平台: 64 WIN 8 + 64 MATLAB R2015a 64 WIN 10 + 64 MATLAB R2015a 不清楚在其它平台下表现如何,有兴趣的朋友不妨也测试一下,让我们一起来找 bug :) |
12 条回复