Formatting binary values in Scala

2020-02-03 05:04发布

Does Scala have a built in formatter for binary data?

For example to print out: 00000011 for the Int value 3.

Writing one won't be difficult - just curious if it exists.

8条回答
我只想做你的唯一
2楼-- · 2020-02-03 05:57

This will print the leading zeroes:

  def int2bin(i: Int, numPos: Int): String = {
    def nextPow2(i: Int, acc: Int): Int = if (i < acc) acc else nextPow2(i, 2 * acc)
    (nextPow2(i, math.pow(2,numPos).toInt)+i).toBinaryString.substring(1)
  }
查看更多
放我归山
3楼-- · 2020-02-03 06:01

The Scala standard library's built-in to-binary-digits String formatters (toBinaryString) for the integer types (Byte, Short, Char, Int, and Long) are very limited. And an implementation for Boolean isn't provided.

Additionally, for Byte and Short, the actual emitted format is wrong for negative values (as both forward to the Int.toBinaryString implementation which then 1 fills out to 32 characters, not the correct widths of 8 and 16 characters respectively).

Also, I have read through every answer here. And I learned quite a bit about the various ways to approach solving this problem. Ultimately, though, there wasn't a drop in solution which "just worked" within my current project. So...

I have created a single method implementation fixing and then enhancing all of the above inconsistencies, errors, and adds missing functionality. Now, if I could only figure out how to get this included in the Standard Library for 2.13 and Scala 3...

The size parameter has three domains of values. See the code comments for more precise details.

  1. size = 0 -> (DEFAULT) zero fill to the bit size of the containing type
  2. size < 0 -> model the default behavior of toBinaryString function already on Byte, Short, Char, Int, and Long - also fix the hidden upcast to Int for both Byte and Short
  3. size > 0 -> caller designated zero fill - ignored if the size is smaller than the length required to capture the 1 digit immediately to the left of the leftmost 0 digit (to preserve the sign)

    def toBinaryString[A <: AnyVal](value: A, size: Int = 0): String = {
      val zerosX64: String = //maximum possible number of leading zeros
        "0" * 64

      val (valueAsBinaryString, typeSize) =
        value match {
          case valueAlmostTyped: Boolean =>
            (if (valueAlmostTyped) "1" else "0", 1)
          case valueAlmostTyped: Byte =>
            (valueAlmostTyped.toByte.toBinaryString.takeRight(8), 8) //take() fixes hidden upcast to Int in Byte.toBinaryString
          case valueAlmostTyped: Short =>
            (valueAlmostTyped.toShort.toBinaryString.takeRight(16), 16) //take() fixes hidden upcast to Int in Short.toBinaryString
          case valueAlmostTyped: Char =>
            (valueAlmostTyped.toChar.toBinaryString, 16)
          case valueAlmostTyped: Int =>
            (valueAlmostTyped.toInt.toBinaryString, 32)
          case valueAlmostTyped: Long =>
            (valueAlmostTyped.toLong.toBinaryString, 64)
          case _ =>
            throw new IllegalArgumentException(s"toBinaryString not implemented for this type [${value.getClass.getSimpleName}] - only implemented for Boolean, Byte, Short, Char, Int, and Long")
        }

      val newSize =
        if (size < 0) //model and fix the behavior of existing toBinaryString function on Byte, Short, Char, Int, and Long, and add for Binary
          valueAsBinaryString.length
        else
          if (size == 0) //zero fill to the bit size of the containing type
            typeSize
          else
            if (valueAsBinaryString.length > size) //possibly override the caller specified custom size value as it is smaller than the resulting valueAsBinaryString itself
              if (valueAsBinaryString.take(valueAsBinaryString.length - size + 1).exists(_ == '0')) //only override if there isn't a zero dropped (which includes protecting the sign by ensuring if all 1s preceded the 0, at least a single one is preserved
                valueAsBinaryString.length
              else //caller specified custom value
                size
            else //caller specified custom value
              size
      ( (
            if (newSize > valueAsBinaryString.length)
              zerosX64.take(newSize - valueAsBinaryString.length)
            else
              ""
        )
        + valueAsBinaryString.takeRight(newSize)
      )
    }
查看更多
登录 后发表回答