正如其他人所提到的,当涉及非正常浮点值时,各种处理器不支持正常速度计算。这要么是一个设计缺陷(如果该行为会损害您的应用程序或造成其他麻烦),要么是一个功能(如果您更喜欢更便宜的处理器或通过不使用门来实现这项工作而启用的硅的替代使用)。
理解为什么在 0.5 处发生转变是很有启发性的:
Suppose you are multiplying by p. Eventually, the value becomes so small that the result is some subnormal value (below 2-126 in 32-bit IEEE binary floating point). Then multiplication becomes slow. As you continue multiplying, the value continues decreasing, and it reaches 2-149, which is the smallest positive number that can be represented. Now, when you multiply by p, the exact result is of course 2-149p, which is between 0 and 2-149, which are the two nearest representable values. The machine must round the result and return one of these two values.
Which one? If p is less than ½, then 2-149p is closer to 0 than to 2-149, so the machine returns 0. Then you are not working with subnormal values anymore, and multiplication is fast again. If p is greater than ½, then 2-149p is closer to 2-149 than to 0, so the machine returns 2-149, and you continue working with subnormal values, and multiplication remains slow. If p is exactly ½, the rounding rules say to use the value that has zero in the low bit of its significand (fraction portion), which is zero (2-149 has 1 in its low bit).
您报告说 0.99f 看起来很快。这应该以缓慢的行为结束。也许您发布的代码并不完全是您使用 .99f 测量快速性能的代码?也许起始值或迭代次数发生了变化?
有多种方法可以解决这个问题。一是硬件具有指定将使用或获得的任何次正规值更改为零的模式设置,称为“非正规为零”或“刷新为零”模式。我不使用 .NET,因此无法建议您如何在 .NET 中设置这些模式。
另一种方法是每次添加一个微小的值,例如
n = (n+e) * param;
where e
is at least 2-126/param
. Note that 2-126/param
should be calculated rounded upward, unless you can guarantee that n
is large enough that (n+e) * param
does not produce a subnormal value. This also presumes n
is not negative. The effect of this is to make sure the calculated value is always large enough to be in the normal range, never subnormal.
Adding e
这样当然会改变结果。但是,例如,如果您正在处理具有某种回声效果(或其他过滤器)的音频,则e
太小,不会造成人类收听音频时可观察到的任何影响。它可能太小,无法在生成音频时导致硬件行为发生任何变化。