最近我在 webkit 资源中发现了这个有趣的东西,与颜色转换(hsl 到 rgb)有关:
http://osxr.org/android/source/external/webkit/Source/WebCore/platform/graphics/Color.cpp#0111
const double scaleFactor = nextafter(256.0, 0.0); // it's here something like 255.99999999999997
// .. some code skipped
return makeRGBA(static_cast<int>(calcSomethingFrom0To1(blablabla) * scaleFactor),
(int)(value * 255.999999)
使用这种技术是否正确?为什么不直接使用像 round(blabla * 255) 这样的东西? 它是C/C++的特性吗?正如我所看到的,严格来说是将返回不总是正确的结果,在 100 个案例中有 27 个。请参阅 https://docs.google.com/spreadsheets/d/1AbGnRgSp_5FCKAeNrELPJ5j9zON9HLiHoHC870PwdMc/edit?usp=sharing 处的电子表格。
有人请解释一下——我认为这应该是一些基本的东西。
最佳答案
通常我们希望将(闭合)区间[0,1]
中的实数x
映射到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)
:这个分区将是公平的,除了一个 sngle 问题:我们将得到值 256(超出范围!)当x=1
[**]
这就是为什么 j = (int)(x * 255.9999...)
(其中 255.9999...
实际上是小于 256 的最大 double 值)会起作用。
另一种实现方式(也是合理的,几乎等同的)是
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
和 x=1
。
[**] IEEE 浮点标准保证这里没有舍入歧义:整数接受精确的浮点表示,乘积将是精确的,并且转换将始终给出 256。此外,我们保证 1.0 * z = z
。
关于c++ - : (int) blabla * 255. 99999999999997 或 round(blabla*255) 常识正确的是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22334364/