为了练习,我在rust中实现了qoi specification.其中有一个小的散列函数来存储最近使用的像素:
指数位置=(r*3+g*5+b*7+a*11)%64
其中r、g、b和a分别为红色、绿色、蓝色和阿尔法通道.
我假设这是一个散列,因为它用mod为数字创建了一个唯一的素数分解,以限制字节数.总之,我在代码中天真地实现了它.
在查看其他实现时,我遇到了一个优化哈希计算的bit hack:
fn hash(rgba:[u8:4]) -> u8 {
let v = u32::from_ne_bytes(rgba);
let s = (((v as u64) << 32) | (v as u64)) & 0xFF00FF0000FF00FF;
s.wrapping_mul(0x030007000005000Bu64.to_le()).swap_bytes() as u8 & 63
}
我想我了解大部分情况,但我对幻数(被乘数)感到困惑.据我所知,它应该被翻转.作为一个逐步的例子:
-
let rgba = [0x12, 0x34, 0x56, 0x78]
. - 在我的机器(little endian)上,
v
的值为0x78563412
. - 位移位使值扩散,得到
s = 0x7800340000560012
. - 这就是我困惑的地方.幻数的值应在64位字段(3、5、7、11)中对齐相乘,间距与原始值相同.然而,它们似乎与值的顺序相反:
0x7800340000560012
0x030007000005000B
相乘时,最高值alpha通道(0x78)似乎被乘以3,而最低值红色通道(0x12)被乘以11.我也不完全确定,为什么这个乘法在乘以不同的2次幂后仍然有效.
我知道字节会被交换到big-endian并被修剪,但直到乘法步骤之后,我才明白这一点.
我知道代码生成了正确的散列,但我不明白为什么会这样.有人能解释一下我错过了什么吗?