【问题标题】:How to compute one's complement using Ruby's bitwise operators?如何使用 Ruby 的位运算符计算补码?
【发布时间】:2010-11-15 03:49:41
【问题描述】:

我想要什么:

assert_equal 6, ones_complement(9)   # 1001 => 0110
assert_equal 0, ones_complement(15)  # 1111 => 0000
assert_equal 2, ones_complement(1)   # 01 => 10

输入的大小不固定为 4 位或 8 位。而是一个二进制流。

我所看到的:

v = "1001".to_i(2)                 => 9

有点翻转运算符~

(~v).to_s(2)                       => "-1010"
sprintf("%b", ~v)                  => "..10110"
~v                                 => -10

我认为它与用于存储符号或其他东西的位有关......有人可以解释这个输出吗?如何在不使用字符串操作的情况下获得一个补码,例如从 sprintf 输出中剪切最后 n 个字符以获得“0110”或将 0 替换为 1,反之亦然

【问题讨论】:

    标签: ruby operators bit-manipulation


    【解决方案1】:

    听起来您只想翻转四位(输入的长度)-因此您可能想与 1111 进行异或。

    【讨论】:

    • P.S.我不做 Ruby,但在 Python 中,9^15 返回 6(即 1001 xor 1111 是 0110)。
    • Ruby 中的 9^15 => 6。谢谢..我的脑细胞似乎已经收工了。需要睡一会儿……我猜。
    • 1^0b1111 == 1,而不是 2。您如何决定使用多少个前导零?
    • 1^15 => 实际上是 14。就像 Rutger 在下面指出的那样,要考虑的 number_of_bits 需要是这个函数的输入参数。
    【解决方案2】:

    请参阅this question 了解原因。

    您的方法的一个问题是,您的预期答案只有在您只翻转四个有效位时才为真:1001 -> 0110。

    但数字存储前导零,并且 ~ 运算符也翻转所有前导位:00001001 -> 11110110。那么前导的 1 被解释为负号。

    在决定如何实现之前,您确实需要指定函数应该如何处理 0b101 和 0b11011 等数字。如果您只想翻转 4 位,则可以按照另一个答案中的建议执行 v^0b1111。但是如果你想翻转所有有效位,它会变得更加复杂。

    编辑

    这是翻转所有有效位的一种方法:

    def maskbits n
      b=1
      prev=n;
      mask=prev|(prev>>1)
      while (mask!=prev)
        prev=mask;
        mask|=(mask>>(b*=2))
      end
      mask
    end
    
    def ones_complement n
      n^maskbits(n)
    end
    

    这给了

    p ones_complement(9).to_s(2)  #>>"110" 
    p ones_complement(15).to_s(2) #>>"0"
    p ones_complement(1).to_s(2)  #>>"0"
    

    这不会为 one_compliment(1) 提供所需的输出,因为它将 1 视为“1”而不是“01”。我不知道该函数如何在不将宽度作为参数的情况下推断出您想要多少个前导零。

    【讨论】:

    • 我确实读过那个问题,但我仍然需要这个功能。更新了更多规格的问题..
    • 寻找有效位的好算法。
    【解决方案3】:

    你在做什么(使用~)运算符,确实是一个补码。由于 Ruby 解释数字的方式,您得到的值是您不期望的。

    您实际需要做什么取决于您使用它的目的。也就是说,为什么需要1的补码?

    【讨论】:

    • 我正在使用它来匹配位流中的模式。我确实需要它:)
    【解决方案4】:

    如果你正在使用字符串,你可以这样做:

    s = "0110"
    s.gsub("\d") {|bit| bit=="1"?"0":"1"}
    

    如果您使用数字,则必须定义有效位数,因为:
    0110 = 6; 1001 = 9;
    110 = 6; 001 = 1;

    即使忽略这个标志,你也可能不得不处理这个问题。

    【讨论】:

    • 是的,这是我的后备方法。我会阅读 104 或 106 的 1 和 0 流……我认为按位表示可能是更合适的选择。不知道字符串选项的性能如何甚至工作..
    【解决方案5】:

    Ruby 只存储一个(签名的)数字。这个数字的内部表示是不相关的:它可能是一个 FixNum、BigNum 或其他东西。因此,数字中的位数也是不确定的:它毕竟只是一个数字。这与例如 C 中的 int 可能是 32 位(固定)相反。

    那么 ~ 操作符是做什么的呢?嗯,就像这样:

    class Numeric
        def ~
            return -self - 1
        end
    end
    

    ...因为当查看 2 的补数时,'~' 代表的就是这个。

    因此,您的输入语句中缺少的是您要切换的位数:32 位 ~ 与通用 ~ 不同,就像在 Ruby 中一样。

    现在,如果您只想对 n 位进行位翻转,您可以执行以下操作:

    class Numeric
        def ones_complement(bits)
            self ^ ((1 << bits) - 1)
        end
    end
    

    ...但是您必须指定要翻转的位数。这不会影响标志标志,因为 XOR 超出了你的能力范围:)

    【讨论】:

    • 必须让 Jefromi 接受.. 因为这个概念和提早:) 你的答案比我破解的更 Rubyish.. 喜欢将方法添加到 Numeric 类。读起来更好。
    • 这对浮点数不起作用,你不应该在Integer 类上定义它吗?
    【解决方案6】:

    请记住,如果你传入一个 Fixnum,你现在得到的是一个的补码:表示数字的位数在解释器中是一个固定的数量,因此在二进制表示的前面有前导 0数字 9(二进制 1001)。您可以通过检查任何 Fixnum 的大小来找到此位数。 (答案以字节为单位返回)

    1.size            #=> 4
    2147483647.size   #=> 4
    

    ~ 也是在 Bignum 上定义的。在这种情况下,它的行为就好像在 Bignum 中指定的所有位都被反转了,然后在该 Bignum 前面有一个无限的 1 字符串。可以想象,您可以将您的比特流推入 Bignum 并反转整个事情。但是,您需要在反转之前知道比特流的大小,以便在反转后得到有用的结果。

    要立即回答问题,您可以找到小于输入的 2 的最大幂,将其加倍,减去 1,然后将结果与您的输入进行异或运算,始终得到一个补码仅输入数字中的有效位。

    def sig_ones_complement(num)
      significant_bits = num.to_s(2).length
      next_smallest_pow_2 = 2**(significant_bits-1)
      xor_mask = (2*next_smallest_pow_2)-1
      return num ^ xor_mask
    end
    

    【讨论】:

    • 这不适用于像 0001(十进制 1)这样的位模式,我希望它是 1110。要考虑的 number_of_bits 必须作为输入传入。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-25
    • 2010-10-21
    • 1970-01-01
    • 2011-05-30
    相关资源
    最近更新 更多