通常我们想要映射一个真实的值x
在(闭)区间内[0,1]
为整数值j
在范围中[0 ...255]
.
我们希望以“公平”的方式做到这一点,这样,如果实数在范围内均匀分布,则离散值将近似等概率:256 个离散值中的每一个都应获得“相同的份额”(1/ 256)从[0,1]
间隔。也就是说,我们想要一个像这样的映射:
[0 , 1/256) -> 0
[1/256, 2/256) -> 1
...
[254/256, 255/256) -> 254
[255/256, 1] -> 255
我们不太关心过渡点 [*],但我们确实希望覆盖整个范围 [0,1]。如何做到这一点?
如果我们简单地这样做j = (int)(x *255)
:值 255 几乎不会出现(仅当x=1
);和其余的值0...254
每个人都会获得间隔的 1/255。这将是不公平的,不管限制点处的舍入行为。
如果我们这样做j = (int)(x * 256)
:这个分区是公平的,除了一个问题:我们会得到值 256(超出范围!)x=1
[**]
这就是为什么j = (int)(x * 255.9999...)
(where 255.9999...
实际上是小于 256 的最大双精度)就可以了。
另一种实现(也是合理的,几乎等效)是
j = (int)(x * 256);
if(j == 256) j = 255;
// j = x == 1.0 ? 255 : (int)(x * 256); // alternative
但这会更加笨拙并且可能效率较低。
round()
在这里没有帮助。例如,j = (int)round(x * 255)
会给整数 1/255 的份额j=1...254
以及该值的一半到极值点j=0
, j=255
.
[*] 我的意思是:我们对“小”邻域(例如 3/256)中发生的情况并不是非常感兴趣:四舍五入可能会得到 2 或 3,这并不重要。但我们对极值感兴趣:我们想要得到 0 和 255,因为x=0
and x=1
分别。
[**] IEEE 浮点标准保证这里没有舍入歧义:整数承认精确的浮点表示,乘积将是精确的,并且转换将始终给出 256。此外,我们保证1.0 * z = z
.