Converting an integer to a base 2 string

(see also Binary representation of numbers)

WJP For mysterious reasons format will print an integer in decimal, hex, or octal, but not binary. Here's a procedure that does. It works for negative integers as well as positive. If the argument is not an integer it throws an exception. It will use a sufficient number of bits regardless of the size of the integer. It takes an optional second argument specifying the number of bits to use. If the required number of bits is larger, this value is ignored. If the specified number of bits is larger than the necessary number of bits, the result is zero-padded to the specified number of bits.

 proc binfmt1 {k {cnt 8}} {
    if {![string is integer $k]} {error "argument is not an integer"}
    #Compute minimum number of bits necessary
    #This is the integer ceiling of the log of the number to the base 2.
    if {$k == 0} {set bits 1} else {set bits [expr int(ceil((log(abs($k)))/(log(2))))]}
    if {$bits > $cnt} {set cnt $bits}
    #Now compute binary representation
    for {set i [expr $cnt -1]} {$i > -1} {incr i -1} {
        append s [expr ($k >> $i) & 1]
    }
    return $s
 }


 proc demo {} {
    puts "binfmt 3         = [binfmt 3]"
    puts "binfmt -3        = [binfmt -3]"
    puts "binfmt 3 0       = [binfmt 3 0]"
    puts "binfmt 3 4       = [binfmt 3 4]"
    puts "binfmt 25        = [binfmt 25]"
    puts "binfmt 1025      = [binfmt 1025]"
    puts "binfmt 69125     = [binfmt 69125]"
    puts "binfmt 69125 24  = [binfmt 69125 24]"
 }

 demo

Here is the output of the demo script, right-justified for expository purposes:

 binfmt 3         =                 00000011
 binfmt -3        =                 11111101
 binfmt 3 0       =                       11
 binfmt 3 4       =                     0011
 binfmt 25        =                 00011001
 binfmt 1025      =              10000000001
 binfmt 69125     =        10000111000000101
 binfmt 69125 24  = 000000010000111000000101

Omar Medina 11-08-2006 Another Variant:

 #--------------------------
 proc pToBinaryStr {vValue} {
   set vBinStr ""
   foreach vVal [split [format %lX $vValue] {}] {
      switch $vVal {
         0 {append vBinStr "0000"}
         1 {append vBinStr "0001"}
         2 {append vBinStr "0010"}
         3 {append vBinStr "0011"}
         4 {append vBinStr "0100"}
         5 {append vBinStr "0101"}
         6 {append vBinStr "0110"}
         7 {append vBinStr "0111"}
         8 {append vBinStr "1000"}
         9 {append vBinStr "1001"}
         a -
         A {append vBinStr "1010"}
         b -
         B {append vBinStr "1011"}
         c -
         C {append vBinStr "1100"}
         d -
         D {append vBinStr "1101"}
         e -
         E {append vBinStr "1110"}
         f -
         F {append vBinStr "1111"}
      }
   }
   return $vBinStr
 }
 #--------------------------

Output:

 #--------------------------
 pToBinaryStr 45
 00101101
 pToBinaryStr 4
 0100
 pToBinaryStr 7777
 0001111001100001
 pToBinaryStr 0x334
 001100110100
 #--------------------------

RS 2006-08-11: Note that the "mysterious reason" mostly is historic: format just wraps C's sprintf() function. Besides that, Tcl, has binary scan and binary format:

 % binary scan [binary format I 3] B* bits; set bits
 00000000000000000000000000000011
 % binary scan [binary format I -3] B* bits; set bits
 11111111111111111111111111111101

So it's just the intended length of the bitstring that you'll have to manipulate, if you so wish :^)

RS 2006-10-04: Hex string of arbitrary length to bits:

 interp alias {} hex2bin {} string map -nocase {
     0 0000 1 0001 2 0010 3 0011 4 0100 5 0101 6 0110 7 0111
     8 1000 9 1001 a 1010 b 1011 c 1100 d 1101 e 1110 f 1111
 }

ferrieux 8.6 has %b, %lb, %llb in format.