如何在Scala中编写二进制字面量?

30

Scala直接支持使用十六进制和八进制数字:

scala> 01267 + 0100
res1: Int = 759

scala> 0x12AF + 0x100
res2: Int = 5039

但是你如何在Scala中将整数表示为二进制数呢?


3
注意,从2.10版本开始,使用前导0表示八进制已被弃用。 - AmigoNico
7个回答

33

如果性能不是问题,你可以使用String类型并将其转换为整数。

val x = Integer.parseInt("01010101", 2)

之所以不直接支持二进制数,是因为你可以轻松地在十六进制和二进制之间进行转换。为了使你的代码更清晰,你可以将二进制数放在注释中。

val x = 0x55 //01010101

4
哇,太糟糕了。我不同意"你可以轻松地在十六进制和二进制之间转换"的理由。如果你想使用二进制表示法,我认为你应该可以这样做,特别是因为Java支持它 =( - Dici
你的代码对我不起作用。在 val x: Int = -2147483648 后面加上 val y: Int = Integer.parseInt(x.toBinaryString, 2) 会导致异常。请注意,x 仍然是一个有效的整数。 - yarchik

20

在2.10版本中,您可以创建一个字符串内插器,例如可以编写b"0010"表示2。使用宏,您可以摆脱关联的运行时开销,并在编译时进行转换。查看Jason Zaugg的macrocosm,以查看其实际应用:

scala> b"101010"
res4: Int = 42

scala> b"102"
<console>:11: error: exception during macro expansion: invalid binary literal
              b"102"
              ^

9
在2.10版本中,使用新的“implicit class”和“value class”机制,您可以编写类似以下内容的代码,以添加方便的方法而无需创建对象开销:
implicit class IntToBase( val digits:String ) extends AnyVal {
  def base(b:Int) = Integer.parseInt( digits, b )
  def b = base(2)
  def o = base(8)
  def x = base(16)
}

这使得您可以做一些类似于:

"555".o  // 365 decimal

不会创建任何IntToBase对象。


这个无法处理二进制补码中的负数:"11111111111111111111111111111111".b => java.lang.NumberFormatException: 输入字符串:"11111111111111111111111111111111"。 - jsears
是的,parseInt() 函数会将前导的 "-" 视为负数的指示符。你所指定的数字没有这个符号,因此 parseInt() 认为你指的是正数,而这个数太大了,无法转换成32位有符号整数,因此会抛出异常。parseInt() 不会将高位比特位置上的1视为负数的指示符(这种解释也是依赖于具体机器的)。 - AmigoNico

6
如果您要转换一个“看起来像”二进制的整数,就需要像@agilesteel建议的那样小心。例如,0101.b会尝试将65十进制转换为二进制(初始0表示八进制),而101.b则会尝试将101十进制转换为二进制。只有从字符串转换才真正有意义,对于这一点,可以使用Integer.parseInt,以及从数字到二进制字符串表示,可以使用Integer.toString(x, 2)
我想不出太多编程二进制文字的用例。话虽如此,它们已经作为带有前缀0b的数字出现在Java 7中,因此如果它们不久后出现在Scala中,我会感到惊讶。然而,Java似乎在没有它们的情况下度过了15年。

10
在Java中没有无符号字符或像样的位运算符也可以“正常”运行。所谓的“正常”是指人们不得不放弃在Java中写那种需要使用这些特性的代码,否则就会一路上对Java进行诅咒。 - Daniel C. Sobral

4
如果您计划经常使用它,则可以通过隐式转换模拟其行为。
object Extensions {
  implicit def conversion(x: Int) = new BinaryInt(x)
  class BinaryInt(x: Int) {
    def b = {
      // Conversion code like Integer.parseInt
      // as Kim suggested
    }
  }
}

现在你可以做像这样的事情。
import Extensions._
val x = 0101.b
// or
val x = 5.b

你必须自己决定,转换的方向应该是什么。

4
小心:0101是一个八进制数! - soc
这是直到2.10的版本。请查看其他评论。 - Jacek Laskowski

2

如果你想获得一个Int的二进制表示的字符串,可以调用4.toBinaryString。填充更加困难。你需要像这样做:4.toBinaryString.reverse.padTo(8, "0").reverse.mkString


0
def _0b(row: Int): Int = {
  row.toString
    .reverse
    .split("")
    .zipWithIndex
    .map(x => (x._1.toInt, x._2))
    .filter(_._1 == 1)
    .map(x => Math.pow(2,x._2).toInt)
    .sum
}

_0b(10011001) = 153

尽管它仅限于32位值


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