最近我在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)
完全使用这种技术是否正确?为什么不直接使用圆形(blabla * 255)? 它是C / C ++的功能吗?正如我所看到的那样严格来说,将返回并不总是正确的结果,在27例中为100.请参见https://docs.google.com/spreadsheets/d/1AbGnRgSp_5FCKAeNrELPJ5j9zON9HLiHoHC870PwdMc/edit?usp=sharing
的电子表格有人请解释 - 我认为它应该是基本的东西。
答案 0 :(得分:25)
通常我们希望将(已关闭)时间间隔x
中的实数值[0,1]
映射到j
范围内的整数值[0 ...255]
。
我们希望以“公平”的方式做到这一点,这样,如果实数均匀分布在范围内,离散值将近似等概率:256个离散值中的每一个应该得到“相同的份额” [0,1]
区间的(1/256)。也就是说,我们想要这样的映射:
[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)
:除了一个问题之外,这个分区是公平的:当x=1
[**]
这就是j = (int)(x * 255.9999...)
(其中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)
将为整数j=1...254
提供1/255的份额,将该值的一半提供给极值点j=0
,j=255
。
[*]我的意思是:我们对3/256的'小'邻域中发生的事情并不十分感兴趣:四舍五入可能会给2或3,这没关系。但是我们对极值感兴趣:我们希望得到0和255,分别为x=0
和x=1
。
[**] IEEE浮点标准保证这里没有舍入歧义:整数允许精确的浮点表示,产品将是精确的,并且转换将始终为256.此外,我们保证{{{ 1}}。
答案 1 :(得分:7)
一般来说,我说(int)(blabla * 255.99999999999997)
比使用round()
更正确。
为什么?
因为使用round()
,0和255只有&#34; half&#34; 1-254的范围。如果你round()
,则0-0.00196078431被映射到0,而0.00196078431-0.00588235293被映射到1.这意味着1的发生概率比0高200%,严格来说,这是一个不公平的偏见。
如果,isntead,一个乘以255.99999999999997然后floor(这是一个整数的转换,因为它截断),那么0到255之间的每个整数都是可能的。
如果您的电子表格以小数百分比计算(例如,如果它每次计算0.01%而不是1%),则可能会更好地显示此信息。 I've made a simple spreadsheet to show this。如果你查看那个电子表格,你会看到0在round()
时不公平地存在偏见,但是使用其他方法时事情是公平和平等的。
答案 2 :(得分:5)
Casting to int与floor函数具有相同的效果(即它会截断)。当你调用它时,好了,舍入到最接近的整数。
他们做不同的事情,所以选择你需要的那个。