Java 中对任意长度的位模式进行符号扩展的最有效方法是什么?
What is the most efficient way in Java to sign extend an arbitrary length pattern of bits?
比方说,我已经将三个 10 位有符号整数打包成一个 Java 整数。
我可以轻松提取 10 位:
int unpacked = packed & 0x3FF;
packed >>= 10;
etc ...
但现在我需要对最高位(右起第 9 位)进行符号扩展。有没有一种快速的方法可以通过测试最高位和设置来做到这一点?
也许有更好的开箱方式,将标牌留在原处。
int signed = (packed << (32 - 10)) >> (32 - 10);
因为>>>
是无符号右移,>>
是有符号右移。
(我还有"bug",谢谢@rghome)
替代两次移动的另一种方法是翻转符号然后减去它:
int unpacked = packed & 0x3FF;
int extended = (unpacked ^ 0x200) - 0x200;
如果符号未设置,翻转它设置它并减去它再次重置它。
如果设置了符号,翻转它会重置它,减去它会再次设置它,但也会一直借到顶部,一路设置所有位。
这有一些优点,
- 代码不依赖于目标整数类型的大小,如果
unpacked
和 extended
是 long
那么同样的事情会起作用。
- XOR 和减法可能会更便宜一些,例如在 Skylake 上,您每个周期可以执行 4 次这些基本操作,但只能轮班 2 次。虽然延迟是相同的,但只有在代码中可用的 ILP 很高时才有意义。
- 移位并不是真正的代数结合,但 XOR 和减法可以。例如,如果下一个操作是向
extended
添加一些常量,那么该添加和 "subtract the sign" 步骤可以合并为一个操作。
比方说,我已经将三个 10 位有符号整数打包成一个 Java 整数。
我可以轻松提取 10 位:
int unpacked = packed & 0x3FF;
packed >>= 10;
etc ...
但现在我需要对最高位(右起第 9 位)进行符号扩展。有没有一种快速的方法可以通过测试最高位和设置来做到这一点?
也许有更好的开箱方式,将标牌留在原处。
int signed = (packed << (32 - 10)) >> (32 - 10);
因为>>>
是无符号右移,>>
是有符号右移。
(我还有"bug",谢谢@rghome)
替代两次移动的另一种方法是翻转符号然后减去它:
int unpacked = packed & 0x3FF;
int extended = (unpacked ^ 0x200) - 0x200;
如果符号未设置,翻转它设置它并减去它再次重置它。
如果设置了符号,翻转它会重置它,减去它会再次设置它,但也会一直借到顶部,一路设置所有位。
这有一些优点,
- 代码不依赖于目标整数类型的大小,如果
unpacked
和extended
是long
那么同样的事情会起作用。 - XOR 和减法可能会更便宜一些,例如在 Skylake 上,您每个周期可以执行 4 次这些基本操作,但只能轮班 2 次。虽然延迟是相同的,但只有在代码中可用的 ILP 很高时才有意义。
- 移位并不是真正的代数结合,但 XOR 和减法可以。例如,如果下一个操作是向
extended
添加一些常量,那么该添加和 "subtract the sign" 步骤可以合并为一个操作。