** Summary ** Methods for converting numbers between non-negative decimal and binary (we're talking about the printable textual representation of binary numbers here, e.g., 6 -> 110, and the reciprocal 110 -> 6). ** See Also ** [Big bitstring operations]: [Converting an integer to a base 2 string]: [to.binary]: [Math on binary strings]: [To little endian]: ** Converting Decimal to Binary ** ====== proc dec2bin {i} { #returns a string, e.g. dec2bin 10 => 1010 set res {} while {\$i>0} { set res [expr {\$i%2}]\$res set i [expr {\$i/2}] } if {\$res == {}} {set res 0} return \$res } ====== In the following example, which requires Tcl 8.6 or later, `%lb` or `%b` could be used, but `\$int` would then be truncated if necessary. Also, in the case of `%lb` or `%b`, if `\$int` is too large, the result could be nonsensical, as a truncated version of the machine representation of a negative integer would be returned. ====== package require Tcl 8.6 proc dec2bin {int} { return [format %llb \$int] } ====== The following example includes a "width" option to set the width of the resulting string ====== proc dec2bin {i {width {}}} { #returns the binary representation of \$i # width determines the length of the returned string (left truncated or added left 0) # use of width allows concatenation of bits sub-fields set res {} if {\$i<0} { set sign - set i [expr {abs(\$i)}] } else { set sign {} } while {\$i>0} { set res [expr {\$i%2}]\$res set i [expr {\$i/2}] } if {\$res == {}} {set res 0} if {\$width != {}} { append d [string repeat 0 \$width] \$res set res [string range \$d [string length \$res] end] } return \$sign\$res } ====== Another variation: use `[[[format]]` to convert to octal (or hexadecimal, for that matter), and `[[[string map]]` to transform octal digits to bit patterns. ====== proc dec2bin x { if {[string index \$x 0] eq {-}} { set sign - set x [string range \$x 1 end] } else { set sign {} } return \$sign[string trimleft [string map { 0 {000} 1 {001} 2 {010} 3 {011} 4 {100} 5 {101} 6 {110} 7 {111} } [format %o \$x]] 0] } ====== And some examples: ====== % dec2bin 9 1001 % dec2bin 255 11111111 ====== Only slightly different from the previous example, by [Lars H]: ====== proc dec2bin {dec} { if {[string match -* \$dec]} { set sign - set dec [expr {abs(\$dec)}] } else { set sign {} } return \$sign[string map { +0 0 +1 1 +2 {10} +3 {11} +4 {100} +5 {101} +6 {110} +7 {111} 0 {000} 1 {001} 2 {010} 3 {011} 4 {100} 5 {101} 6 {110} 7 {111} } [format +%o \$dec]] } ====== The following example, which came from [Googol magnitude] is similar in spirit, but much slower: ====== proc dec2bin {dec} { while {[regexp {[0-9]} \$dec]} { set dec\ [string map {o0 0 o1 1 o2 2 o3 3 o4 4 i0 5 i1 6 i2 7 i3 8 i4 9 0 {}}\ [string map {0 0o 1 0i 2 1o 3 1i 4 2o 5 2i 6 3o 7 3i 8 4o 9 4i} \$dec]] } string map {i 1 o 0} \$dec } ====== The idea of dec2bin is to alternate between a normal decimal notation and an a bi-quinary [http://en.wikipedia.org/wiki/Bi-quinary_coded_decimal] or "abacistic" notation where there are separate positions for fives ('''i''' = a five, '''o''' = no five). It is easy to generate the abacistic notation for half of a number in decimal notation -- just half every digit in place, e.g. 7 -> 3i -- and also easy to convert from abacistic to decimal notation, e.g. i3 -> 8. (Note that this doesn't combine '''i'''s and '''o'''s into the same decimal position as they were split off from.) '''i'''s and '''o'''s that don't have a digit to their right to recombine with constitute the bits that have been shifted out from the decimal number. bin2dec does the same thing backwards, with an extra '''h''' in the leftmost position to discard unnecessary zeroes. Another alternative is to use `[[[binary]]`. The general idea is illustrated in this simple example, followed by a more complete example: ====== proc dec2bin {int} { set binRep [binary format c \$int] binary scan \$binRep B* binStr return [string trimleft \$binStr 0] } ====== In the complete example, the bytes a list of numbers representing the individual bytes of the machine binary representation are built up, in little-ending order. The list is then reversed so that it is in big-endian order, and then scanned: ====== proc dec2bin {i} { #returns the binary representation of \$i e.g. dec2bin 10 => 1010 #this did not work properly where \$i > [expr {2**32-1}] #binary scan [binary format I1 \$i] B* x if {\$i == 0} { return 0 } elseif {\$i > 0} { set sign {} } else { set sign - set i [expr {abs(\$i)}] } while {\$i>0} { set mod [expr {\$i % 256}] set i [expr {\$i/256}] lappend mods \$mod } binary scan [binary format c* [lreverse \$mods[set mods {}]]] B* x return \$sign[string trimleft \$x 0] } ====== ** Converting Binary to Decimal ** [KBK]: contributed the precursor to this variant in the [Tcl chatroom] on 2002-11-18: ====== proc bin2dec {bin} { if {\$bin == 0} { return 0 } elseif {[string match -* \$bin]} { set sign - set bin [string range \$bin 1 end] } else { set sign {} } if {[string map [list 1 {} 0 {}] \$bin] ne {}} { error "argument is not in base 2: \$bin" } set r 0 foreach d [split \$bin {}] { incr r \$r incr r \$d } return \$sign\$r } ====== ====== proc bin2dec {bin} { if {\$bin == 0} { return 0 } elseif {[string match -* \$bin]} { set sign - set bin [string range \$bin[set bin {}] 1 end] } else { set sign {} } return \$sign[expr 0b\$bin] } ====== ====== proc bin2dec {bin} { #returns integer equivalent of \$bin set res 0 if {\$bin == 0} { return 0 } elseif {[string match -* \$bin]} { set sign - set bin [string range \$bin[set bin {}] 1 end] } else { set sign {} } foreach i [split \$bin {}] { set res [expr {\$res*2+\$i}] } return \$sign\$res } ====== ====== proc bin2dec {bin} { if {\$bin == 0} { return 0 } elseif {[string match -* \$bin]} { set sign - set bin [string range \$bin[set bin {}] 1 end] } else { set sign {} } set res 0 for {set j 0} {\$j < [string length \$bin]} {incr j} { set bit [string index \$bin \$j] set res [expr {\$res << 1}] set res [expr {\$res | \$bit}] } return \$sign\$res } ====== ====== proc bin2dec {num} { set num h[string map {1 i 0 o} \$num] while {[regexp {[io]} \$num]} { set num\ [string map {0o 0 0i 1 1o 2 1i 3 2o 4 2i 5 3o 6 3i 7 4o 8 4i 9 ho h hi h1}\ [string map {0 o0 1 o1 2 o2 3 o3 4 o4 5 i0 6 i1 7 i2 8 i3 9 i4} \$num]] } string range \$num 1 end } ====== `[[[binary]]` could also be used: ====== proc bin2dec {bin} { #returns decimal representation of the binary representation \$bin if {\$bin == 0} { return 0 } elseif {[string match -* \$bin]} { set sign - set bin [string range \$bin 1 end] } else { set sign {} } set mod [expr {[string length \$bin]%8}] if {\$mod} { set mod [expr {8-\$mod}] } set bin [string repeat 0 \$mod]\$bin set len [string length \$bin] set bin [binary format B* \$bin] #the else block ould do it all, but for illustration... if {\$len<=8} { binary scan \$bin cu res } elseif {\$len <=16} { binary scan \$bin Su res } elseif {\$len <=32} { if {\$len <= 24} { set bin [binary format B* 0]\$bin } binary scan \$bin Iu res } else { set res 0 set blen [expr {\$len/8}] set pos -1 while {\$blen} { incr blen -1 binary scan \$bin x[incr pos]cu next set res [expr {\$res + \$next*(2**(\$blen*8))} ] } } return \$sign\$res } ====== ** Misc ** ---- contributors: [RS], Joseph Collard, [Eric Amundsen], [PYK] <> Arts and crafts of Tcl-Tk programming | Mathematics | Binary Data