7

最近我在 webkit 资源中发现了这个有趣的东西,与颜色转换(hsl to 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上的电子表格

有人请解释一下——我认为它应该是基本的。

4

3 回答 3

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

我们不太关心过渡点 [*],但我们确实希望覆盖整个范围 [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...)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 和x=0255 x=1

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

于 2014-03-11T19:20:21.427 回答
7

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

为什么?

因为round(), 0 和 255 只具有 1-254 范围的“一半”。如果你round(),那么 0-0.00196078431 被映射到 0,而 0.00196078431-0.00588235293 被映射到 1。这意味着 1 发生的概率比 0 高 200%,严格来说,这是一个不公平的偏差。

如果,不是,一个乘以 255.99999999999997,然后乘以楼层(这是转换为整数所做的,因为它会截断),那么从 0 到 255 的每个整数都是同样可能的。

如果您的电子表格以分数百分比计算(即,如果每次以 0.01% 而不是 1% 计算),则电子表格可能会更好地显示这一点。我制作了一个简单的电子表格来显示这一点。如果您查看该电子表格,您会发现 0 对 when round()ing 存在不公平的偏见,但使用其他方法,事情是公平和平等的。

于 2014-03-11T19:25:28.090 回答
5

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

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

于 2014-03-11T19:14:04.093 回答