例如,我在这个gamedev.net线程中发现了用户“ eq”的注释:
我更喜欢将较高的位复制到未定义的较低位位:
R8 =(R5 << 3)| (R5 >> 2);
但是我不明白其背后的原因。
将那些位复制到转换后的数据中有什么用?
#1 楼
如果不复制这些位,则LSB将为0,因此对于最大值0x1f(5位为最大值),当转换为8位时,它将扩展为0xf8。您想要的是0xff,因此0x00-> 0x1f的范围将映射到0x00-> 0xff而不是0x00-> 0xf8。如果不合并LSB,您将无法将0x1f,0x1f,0x1f转换为白色(0xff,0xff,0xff)。顺便说一下,这与N * 0xff / 0x1f相同。Example:
left shift only (<< 3)
%---00001 -> %00001000 (0x01 -> 0x08)
%---10000 -> %10000000 (0x10 -> 0x80)
%---11111 -> %11111000 (0x1f -> 0xf8)
merging MSB to LSB
%---00001 -> %00001000 (0x01 -> 0x08)
%---10000 -> %10000100 (0x10 -> 0x84)
$---11111 -> %11111111 (0x1f -> 0xff)
#2 楼
实际上,进行位复制有一个很好的数学理由:首先请注意,n位字符串$ N $实际上表示值$ \ frac {N} {2 ^ n-1} $,我们要生成m位字符串$ M $,其中$ n
我们首先用
$$ \ frac {N.(2 ^ n + 1)} {(2 ^ n-1)( 2 ^ n + 1)} \ approx \ frac {M} {2 ^ m-1} $$
,这简化为
$$ \ frac {N.(2 ^ n + 1)} {2 ^ {2n} -1} \大约\ frac {M} {2 ^ m-1} $$
在您的情况下,$ n \ in \ {5,6 \} $和$ m = 8 $,我们可以在这里“停止”,但是如果m >> n,则可以重复该过程(引起恶心)。
我们接下来进行近似...
$$ \ frac {N.(2 ^ n + 1)} {2 ^ {2n}} \约\ frac {M} {2 ^ m} $$简化为
$$ \ frac { N.(2 ^ n + 1)} {2 ^ {2n-m}} \大约M $$
请注意,$ N。(2 ^ n + 1)$等同于重复n位字符串,以创建2n位字符串,该除法将$ 2nm $ LSB移出,剩下M位结果。
QED
当然,“正确”的计算是$ M = \ lfloor(\ frac {(2 ^ m-1)N} {2 ^ n-1 } + \ frac {1} {2} \ rfloor $,但是这种近似值通常在大多数情况下都有效。当然,有时它不准确,但是IIRC只是一点点,而且相对不常见。
评论
$ \ begingroup $
感谢您使用精美的公式进行详细说明。我对近似值引入的误差感到好奇,因此我制作了这张图表,将两个公式进行了比较:desmos.com/calculator/cvaqofyvbf。但是,我更喜欢PaulHK的回答,因为它更容易理解。
$ \ endgroup $
– wip
18-10-25在2:12
$ \ begingroup $
较小的quibble,如果m> = 2n,则需要更改“近似”方程。举一个极端的例子,如果n = 1,则您需要将字符串重复8次(即执行log2(8)= 3个步骤)。当然,如果您使用“ 10 ... 0”而不是全零填充,则平均而言,您将得到一个较低的误差,但会失去极限。 “但是我更喜欢PaulHK的回答” :-)嗯,没有考虑到口味8P。
$ \ endgroup $
–西蒙F
18-10-25在11:54
评论
如果不复制这些位,则LSB将为0,因此对于最大值0x1f(5位为最大值),当转换为8位时,它将扩展为0xf8。您想要的是0xff,因此0x00-> 0x1f的范围将映射到0x00-> 0xff而不是0x00-> 0xf8。@PaulHK请发表答案。就目前而言,它是完整的,但是作为注释,它是不可搜索的。
是的,谢谢@PaulHK这可以正确回答我的问题