通过移位、反转和加 +/- 1 构造十六进制数
Constructing a hex number by shifting, inverting and adding +/- 1
我正在尝试将数字 0xFD00 保存在寄存器中(MIC-1 架构)。有寄存器:
- 0
- +1
- -1
- AMASK: 0x0FFF
- 掩码:0x00FF
我可以进行左移、右移和反转。也可以添加类似 SMASK + SMASK 或 AMASK + (-1) 的值。我可以用 inverse(AMASK) 得到 0xF000,但我不确定如何在没有太多步骤的情况下得到 0xFD00。
迄今为止最高效的序列是@Falk Hüffner's answer,3 个操作。 (尽管它涉及一个 +
,它不加 +1 或 -1,或者不给它自己加一个值)。
这个答案显示了一种方法,它采用问题标题中明确提到的 4 种操作。
0xFD 是 0b11111101
。所以它有一堆连续的位,还有一个杂散的设置位。一种简单的方法是
- 从 all-ones(
-1
、~0
或 0 - 1
)开始
- 左移2得到
...11111100
- 加 1 得到
...11111101
(0x...FD)
- 移位到寄存器的顶部,移入低位 0 并移出高位 1,留下
0xFD
在顶部,有 8 个低位零。
我不了解 MIC-1,但从您的描述来看,它似乎可以执行这些步骤。如果一次只能移动 1 个位置,则总共需要 2 + 8 条移位指令。可能有其他方法可以更有效地构造此常量,也许是我没有想到的东西,或者可能是机器具有的某些功能。
利用AMASK / SMASK和add / sub carry-propagation的方式可以分别翻转1 / 0位的序列,以及Aki的观察~0xfd00
= 0x02ff
,我们可以做到以下几点:
initial AMASK = 0x00FF
AMASK += 1 (0x0100)
AMASK += AMASK (0x0200) (left shift)
AMASK += SMASM (0x02FF)
NOT AMASK (0xFD00)
请参阅 https://catonmat.net/low-level-bit-hacks,了解您可以使用按位运算进行的各种恶作剧的精彩介绍。 (尽管其中许多还需要 AND、OR 或 XOR。例如,通过 x &= (x-1)
清除最低设置位)
(相关:x86 SIMD 向量的 What are the best instruction sequences to generate vector constants on the fly?:类似的问题,您可以廉价地即时生成 -1
而无需从内存加载,并通过左移等各种其他指令提供它,( SSSE3) 绝对值。但只对短序列值得做,否则只需从内存或 mov-immediate 加载到整数寄存器和 movd xmm0, eax
)
十六进制只是一种在文本中表示数字的方式,例如ASCII。 你可以称之为序列化格式。
0xFD00
只是 64768
(基数 10)或 0b1111110100000000
(基数 2)的另一种写法。因此,您只是在移位和 inc/dec 之外的寄存器中构造 一个数字 。假设你的 bit-shifts 乘以/除以 2,而不是 10 或 16,这些是 binary 运算,所以这是一个二进制数。用像十六进制这样的紧凑格式来表达所需的二进制数只是很方便,但在任何时候你实际上都不需要十六进制,比如一串 base-16 ASCII 数字。
当你在寄存器中构造它时,它不是一个“十六进制数”,它只是一个数字。 如果有的话,它是二进制的。
0xFD00 的倒数 == 0b00000010 11111111 = 0x02ff
这可以通过 SMASK = 0x00FF * 3 + 2 或简单地使用 SMASK | 来实现(1 << 9),如果可用的话。
a = smask
b = smask << 1
a = a + b
a++
a++
return ~a
假设加法可以用两个寄存器实现,而不仅仅是一个常量,左移只是删除从 16 位字中移出的位:
t = ~SMASK // 0xff00
return t + (t << 1)
如果可以通过任意常数而不仅仅是 1 进行移位,则另一种选择是
t = SMASK
t += -1
t += -1
t <<= 8
我正在尝试将数字 0xFD00 保存在寄存器中(MIC-1 架构)。有寄存器:
- 0
- +1
- -1
- AMASK: 0x0FFF
- 掩码:0x00FF
我可以进行左移、右移和反转。也可以添加类似 SMASK + SMASK 或 AMASK + (-1) 的值。我可以用 inverse(AMASK) 得到 0xF000,但我不确定如何在没有太多步骤的情况下得到 0xFD00。
迄今为止最高效的序列是@Falk Hüffner's answer,3 个操作。 (尽管它涉及一个 +
,它不加 +1 或 -1,或者不给它自己加一个值)。
这个答案显示了一种方法,它采用问题标题中明确提到的 4 种操作。
0xFD 是 0b11111101
。所以它有一堆连续的位,还有一个杂散的设置位。一种简单的方法是
- 从 all-ones(
-1
、~0
或0 - 1
)开始 - 左移2得到
...11111100
- 加 1 得到
...11111101
(0x...FD) - 移位到寄存器的顶部,移入低位 0 并移出高位 1,留下
0xFD
在顶部,有 8 个低位零。
我不了解 MIC-1,但从您的描述来看,它似乎可以执行这些步骤。如果一次只能移动 1 个位置,则总共需要 2 + 8 条移位指令。可能有其他方法可以更有效地构造此常量,也许是我没有想到的东西,或者可能是机器具有的某些功能。
利用AMASK / SMASK和add / sub carry-propagation的方式可以分别翻转1 / 0位的序列,以及Aki的观察~0xfd00
= 0x02ff
,我们可以做到以下几点:
initial AMASK = 0x00FF
AMASK += 1 (0x0100)
AMASK += AMASK (0x0200) (left shift)
AMASK += SMASM (0x02FF)
NOT AMASK (0xFD00)
请参阅 https://catonmat.net/low-level-bit-hacks,了解您可以使用按位运算进行的各种恶作剧的精彩介绍。 (尽管其中许多还需要 AND、OR 或 XOR。例如,通过 x &= (x-1)
清除最低设置位)
(相关:x86 SIMD 向量的 What are the best instruction sequences to generate vector constants on the fly?:类似的问题,您可以廉价地即时生成 -1
而无需从内存加载,并通过左移等各种其他指令提供它,( SSSE3) 绝对值。但只对短序列值得做,否则只需从内存或 mov-immediate 加载到整数寄存器和 movd xmm0, eax
)
十六进制只是一种在文本中表示数字的方式,例如ASCII。 你可以称之为序列化格式。
0xFD00
只是 64768
(基数 10)或 0b1111110100000000
(基数 2)的另一种写法。因此,您只是在移位和 inc/dec 之外的寄存器中构造 一个数字 。假设你的 bit-shifts 乘以/除以 2,而不是 10 或 16,这些是 binary 运算,所以这是一个二进制数。用像十六进制这样的紧凑格式来表达所需的二进制数只是很方便,但在任何时候你实际上都不需要十六进制,比如一串 base-16 ASCII 数字。
当你在寄存器中构造它时,它不是一个“十六进制数”,它只是一个数字。 如果有的话,它是二进制的。
0xFD00 的倒数 == 0b00000010 11111111 = 0x02ff
这可以通过 SMASK = 0x00FF * 3 + 2 或简单地使用 SMASK | 来实现(1 << 9),如果可用的话。
a = smask
b = smask << 1
a = a + b
a++
a++
return ~a
假设加法可以用两个寄存器实现,而不仅仅是一个常量,左移只是删除从 16 位字中移出的位:
t = ~SMASK // 0xff00
return t + (t << 1)
如果可以通过任意常数而不仅仅是 1 进行移位,则另一种选择是
t = SMASK
t += -1
t += -1
t <<= 8