什么是常识是正确的:(int)blabla * 255.99999999999997或round(blabla * 255)?

时间:2014-03-11 19:09:13

标签: c++ c casting rounding floor

最近我在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), 

我在这里发现:http://www.filewatcher.com/p/kdegraphics-4.6.0.tar.bz2.5101406/kdegraphics-4.6.0/kolourpaint/imagelib/effects/kpEffectHSV.cpp.html

(int)(value * 255.999999)

完全使用这种技术是否正确?为什么不直接使用圆形(blabla * 255)? 它是C / C ++的功能吗?正如我所看到的那样严格来说,将返回并不总是正确的结果,在27例中为100.请参见https://docs.google.com/spreadsheets/d/1AbGnRgSp_5FCKAeNrELPJ5j9zON9HLiHoHC870PwdMc/edit?usp=sharing

的电子表格

有人请解释 - 我认为它应该是基本的东西。

3 个答案:

答案 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=0j=255

enter image description here

[*]我的意思是:我们对3/256的'小'邻域中发生的事情并不十分感兴趣:四舍五入可能会给2或3,这没关系。但是我们对极值感兴趣:我们希望得到0和255,分别为x=0x=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函数具有相同的效果(即它会截断)。当你调用它时,好了,舍入到最接近的整数。

他们做不同的事情,所以选择你需要的那个。