【问题标题】:What is correct by common sense: (int) blabla * 255.99999999999997 or round(blabla*255)?什么是正确的常识:(int)blabla * 255.99999999999997或round(blabla * 255)?
【发布时间】:2014-03-11 19:09:12
【问题描述】:

最近我在 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 的电子表格

请有人解释一下——我认为这应该是一些基本的东西。

【问题讨论】:

  • 您可能忘记了舍入误差,在某些情况下,浮点领域中的乘法可能会返回一个值,如果经过舍入,可能会高于或低于舍入前的情况。跨度>
  • 一次截断和一轮 - 正确取决于预期的内容,尚不清楚。
  • @πάνταῥεῖ:这是一篇很棒的文章,但没有人真正阅读它,而且它在这里并不是特别相关。
  • @Cornstalks 我同意这与所讨论的特定行为无关。

标签: c++ c casting rounding floor


【解决方案1】:

通常我们希望将(闭合)区间[0,1] 中的实数值x 映射到[0 ...255] 范围内的整数值j

我们希望以“公平”的方式进行,这样,如果实数均匀分布在范围内,离散值将近似等概率: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):这个分区将是公平的,除了一个单一的问题:当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

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

round() 在这里没有帮助。例如,j = (int)round(x * 255) 将给整数j=1...254 分配 1/255 的份额,并将该值的一半分配给极值点 j=0j=255

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

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

【讨论】:

  • 优秀。很好的答案。
  • 为那个漂亮的图形+1。 (旁注:请问你是用什么做的?)
  • @Cornstalks : 没有什么幻想,仅仅是 Paint.NET :-)
  • @啊,古老的“手工制作”策略,嗯?我在想你可能做了一些类似xkcd/974xkcd/1319 的事情。
  • 谢谢!我们相信正义。
【解决方案2】:

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

为什么?

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

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

如果您的电子表格以小数百分比计算(即每次以 0.01% 而不是 1% 计算),电子表格可能会更好地显示这一点。 I've made a simple spreadsheet to show this。如果您查看该电子表格,您会发现 0 对 round()ing 时存在不公平的偏见,但在其他方法中,事情是公平和平等的。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 那么为什么 webkit 会选择不直观的(int)(value * 255.999999) 而不是更直观的round(value * 255)
    • @MooingDuck,我的猜测和你的一样糟糕。您必须询问编写程序的人。
    猜你喜欢
    • 1970-01-01
    • 2017-12-09
    • 1970-01-01
    • 1970-01-01
    • 2020-02-05
    • 2014-04-08
    • 1970-01-01
    • 1970-01-01
    • 2018-01-26
    相关资源
    最近更新 更多