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

gaR*_*Rex 7 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), 
Run Code Online (Sandbox Code Playgroud)

同样我在这里找到: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)
Run Code Online (Sandbox Code Playgroud)

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

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

leo*_*loy 25

通常我们希望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
Run Code Online (Sandbox Code Playgroud)

我们并不太关心过渡点[*],但我们确实希望覆盖整个范围[0,1].怎么做到这一点?

如果我们只是这样做j = (int)(x *255):值255几乎永远不会出现(仅当时x=1); 其余的值0...254将分别获得间隔的1/255.无论极限点的舍入行为如何,这都是不公平的.

如果我们改为j = (int)(x * 256):这个分区是公平的,除了一个sngle问题:当x=1[**] 时我们会得到值256(超出范围!)

这就是为什么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
Run Code Online (Sandbox Code Playgroud)

但这会更笨拙,可能效率更低.

round()在这里没有帮助.例如,对于j = (int)round(x * 255)整数给出1/255的份额j=1...254,给极值点给出一半的值j=0,j=255.

在此输入图像描述

[*]我的意思是:我们对3/256的"小"邻域中发生的事情并不十分感兴趣:四舍五入可能会给出2或3,这没关系.但我们感兴趣的极值:我们希望得到0和255,用于x=0x=1分别.

[**] IEEE浮点标准保证这里没有舍入歧义:整数允许精确的浮点表示,产品将是精确的,并且转换将始终为256.此外,我们保证1.0 * z = z.


Cor*_*lks 7

一般来说,我认为(int)(blabla * 255.99999999999997)比使用更正确round().

为什么?

因为round(),0和255只有1-254的"半"范围.如果你round(),则0-0.00196078431被映射到0,而0.00196078431-0.00588235293被映射到1.这意味着1的发生概率比0高200%,严格来说,这是一种不公平的偏见.

如果,isntead,一个乘以255.99999999999997然后floor(这是一个整数的转换,因为它截断),那么从0到255的每个整数都是相同的.

如果以小数百分比计算,您的电子表格可能会更好地显示(例如,如果它每次计算0.01%而不是1%).我已经制作了一个简单的电子表格来展示这一点.如果你看一下那个电子表格,你会看到0在进行时是不公平的偏见round(),但是使用其他方法,事情是公平和平等的.


rub*_*nvb 5

转换为int与floor函数具有相同的效果(即它会截断).当你绕它调用时,舍入到最接近的整数.

他们做不同的事情,所以选择你需要的东西.