c++ - : (int) blabla * 255. 99999999999997 或 round(blabla*255) 常识正确的是什么?

标签 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)

使用这种技术是否正确?为什么不直接使用像 round(blabla * 255) 这样的东西? 它是C/C++的特性吗?正如我所看到的,严格来说是将返回不总是正确的结果,在 100 个案例中有 27 个。请参阅 https://docs.google.com/spreadsheets/d/1AbGnRgSp_5FCKAeNrELPJ5j9zON9HLiHoHC870PwdMc/edit?usp=sharing 处的电子表格。

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

最佳答案

通常我们希望将(闭合)区间[0,1]中的实数x映射到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):这个分区将是公平的,除了一个 sngle 问题:我们将得到值 256(超出范围!)当x=1 [**]

这就是为什么 j = (int)(x * 255.9999...)(其中 255.9999... 实际上是小于 256 的最大 double 值)会起作用。

另一种实现方式(也是合理的,几乎等同的)是

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

enter image description here

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

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

关于c++ - : (int) blabla * 255. 99999999999997 或 round(blabla*255) 常识正确的是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22334364/

相关文章:

c++ - QTreeWidget 子类,停止放置指示器显示给定的不需要的 dropIndicatorPosition

c - 你为什么用汇编编程?

将变量转换为结构定义中定义的 union 类型

swift - 在 Swift 中,你能写一个扩展,它返回扩展所在类的类型化实例吗?

c++ - 我的转置函数有什么问题?

c++ - C/C++ Hex char* 到字节数组

c++ - OpenCV(3.4.1) 错误 : Null pointer

c - 是否可以通过 getopt_long 参数列表向后移动?

c - += 和 =+ C 赋值运算符有什么区别

java - 通用类型转换/实例检查/排除类型参数