在Ruby中将整数转换为二进制

3
我正在解决一道Codewars的难题,但是有两个测试用例无法通过。
这道题的描述如下:将整数转换为二进制形式。你会得到一个整数作为参数,并需要返回它的二进制形式。如果你不知道如何将十进制数转换为二进制数,请点击这里了解。
注意:负数应该被处理为二进制补码;假设所有数字都是整数,在任何语言中使用4个字节(或32位)存储。
我的代码如下:
def to_binary(n)
  temp_array = []
  if n == 0
    temp_array << 0
  elsif n < 0
    n = n % 256
    while n > 0 do
      temp_array << (n % 2)
      n = (n / 2)
    end
    while temp_array.length < 32 do
      temp_array << 1
    end
  else
    while n > 0 do
      temp_array << (n % 2)
      n = (n / 2)
    end
  end
  binary = temp_array.reverse.join
end

测试用例如下:
Test Passed: Value == "10"
Test Passed: Value == "11"
Test Passed: Value == "100"
Test Passed: Value == "101"
Test Passed: Value == "111"
Test Passed: Value == "1010"
Test Passed: Value == "11111111111111111111111111111101"
Test Passed: Value == "0"
Test Passed: Value == "1111101000"
Test Passed: Value == "11111111111111111111111111110001"
Expected: "11111111111111111111110000011000", instead got: "11111111111111111111111111111000"
Expected: "11111111111100001011110111000001", instead got: "11111111111111111111111111000001"
Test Passed: Value == "11110100001000111111"

我怀疑测试失败是因为负整数,因为第一个失败的测试的预期输出是11111111111111111111110000011000,这意味着正参数值为4294966296或者它是负数。如果我运行to_binary(4294966296),我得到了预期的输出。


2
我猜你想写这个算法,即使 Ruby 已经有一个内置的转换方法 10.to_s(2) => "1010" - aarti
1
@500_error 除外,(-10).to_s(2) => "-1010" - lurker
@lurker 考虑到负二进制值实际上并不存在,这是一个合理的解释。表示这些值的常见符号是补码,但这并不是唯一的方法。 - tadman
@tadman 当然可以,但是我想说这并不能解决原帖作者寻找32位2的补码结果的问题。 - lurker
啊,刚看到那部分。你可以在 to_s(2) 之前对值进行一些操作来解决问题。 - tadman
@潜水者 [-10].pack('L').unpack('L')[0].to_s(2) 将给出正确的表示形式 - Anand
2个回答

8

我不太喜欢这种方法,因为我相信有一种更聪明、更紧凑的Ruby式的方法可以实现它。但是使用您的将二进制数字加载到数组中,然后连接的方法,可以以更为简单的方式完成:

def to_binary(n)
  return "0" if n == 0

  r = []

  32.times do
    if (n & (1 << 31)) != 0
      r << 1
    else
      (r << 0) if r.size > 0
    end
    n <<= 1
  end

  r.join
end

或者,使用 @500_error 的建议:
def to_binary(n)
  if n >= 0
    n.to_s(2)
  else
    31.downto(0).map { |b| n[b] }.join
  end
end

处理负数和非负数的不对称性有些令人烦恼。您可以尝试以下方法:
def to_binary(n)
  31.downto(0).map { |b| n[b] }.join.sub(/^0*/, "")
end

32.downto(0).map { |n| -4294966296[n] }.join => "100000000000000000000001111101000" - aarti
@500_error 是的,这很好。唯一剩下的问题是,例如,32.downto(0).map { |n| 2[n] }.join => "00000000000000000000000000000010"但是OP期望得到"10"。 - lurker
是的,这很聪明,但不确定它是否是 OP 的使用情况的好答案。应该进行一些位移操作,以便理解转换过程。 - aarti
@OggimusPrime 谢谢你发现了这个问题。就像软件行业所说的那样,“你总是差1”。 :) 我会修正答案,以免误导其他读者。 - lurker
@lurker 真的,在那个 off by 1 的问题上。再次感谢 :) - Ryan D Haase
显示剩余2条评论

1
这不是一个传统的生成二进制补码的算法,所以我不确定它是否有助于您理解二进制转换,但您可以在Ruby中执行此操作以帮助检查答案。
注意:这仅适用于负数。
32.downto(0).map { |n| -4294966296[n] }.join
=> "100000000000000000000001111101000"

对于二进制补码的计算,最好使用像C这样的低级语言进行实现,以获得算法的感觉。聪明的方法掩盖了步骤,只给出答案。

假设我们使用8位(为简单起见),并且我们想找出如何用二进制补码表示-28。

  1. 首先,我们用二进制形式写出28。 00011100

  2. 然后,我们反转每个数字。0变成1,1变成0。 11100011

  3. 接下来,我们加上1。 11100100


1
使用这个漂亮的表达式 32.downto(0).map { |n| 2[n] }.join => "00000000000000000000000000000010" 得到的结果是 "00000000000000000000000000000010" 而不是 "10"。也许有一个小修改可以修复它。 - lurker
@lurker 你可以将其转换为整数以消除前导零... x = 2; 32.downto(0).map { |n| x[n] }.join.to_i 如果你坚持结果是字符串,只需将其转换回来 x = 2; 32.downto(0).map { |n| x[n] }.join.to_i.to_s - SteveTurczyn
@SteveTurczyn 是的,完全正确。我假设 OP 需要一个字符串输出(因为他们想要输出作为“二进制”表示)。 - lurker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接