Tcl: Convert an integer into binary string of arbitrary length

Viewed 95

I need a way to convert integer from decimal form to binary form. I must be possible to specify the bit length of the output bit string. If the length is more than is required for input representation then the extra bits will be padded with 0 or 1 depending on the sign of the input. If the specified bit length is too small for the input number then the procedure must generate an error.

I believe that a solution should exist with the "binary format" procedure in TCL but it has me confused. It seem that the procedure converts into hexadecimal. I am not sure if TCL can do direct conversion the way I need using built-in functions, or I will have to write a custom procedure and do it step by step.

2 Answers

If you're using a version of tcl too old to support the %b format, you can emulate it with a proc that looks at one bit at a time:

# Positive numbers only
proc num2binary {n width} {
    set bin ""
    while {$n > 0} {
        # Or set bin [string cat [expr {$n & 1}] $bin] in 8.6
        set bin "[expr {$n & 1}]$bin" 
        set n [expr {$n >> 1}]
    }
    format "%0*s" $width $bin
}

puts [num2binary 42 10]

This version uses bit operations and strings instead of the modulo/division and lists like Glenn's. Might be more efficient if that's a concern.

The format command can express a number as binary:

set num 42
set width 10
puts [format "%0*b" $width $num]
# => 0000101010

set num -42
puts [format "%0*b" $width $num]
# => 1111111111111111111111111111111111111111111111111111111111010110

format is Tcl's sprintf


%b was introduced in Tcl 8.6.

This code was adapted from http://rosettacode.org/wiki/Binary_digits#Tcl

proc num2bin {num width} {
    binary scan [binary format "I" $num] "B*" binval
    return [string range $binval end-$width end]
}

num2bin 42 10
# => 00000101010

Truth be told, I find binary format/scan perplexing too.


Another approach:

set num 42
set wid
set bin_digits {}

while {$num > 0} {
    lappend bin_digits [expr {$num % 2}]
    set n [expr {$num / 2}]
}
set binval [join [lreverse $bin_digits] ""]

puts [format {%0*s} $width $binval]
Related