Binary representation of numbers presents various 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 inverse 110 -> 6).
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 }
The following example, which requires Tcl 8.6 or later, uses %llb instead of %lb or %b, because those latter two are subject to overflow: A result could be nonsensical in the case of a large number, since 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 eq {}} {set res 0} if {$width ne {}} { 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 [L1 ] 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 is and os into the same decimal position as they were split off from.) is and os 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] }
Comment: In the last two examples, dec2bin 0 would result in an empty string since string trimleft trims also a single 0. You may replace it e.g. with regsub {^0+([01]+?)$} $x {\1} b; return $sign$b.
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 }
############################################## sigbinfrac2dec.tcl ## ##format Sign(1bit)Integer(8bit).fractional(2bit) ## X XXXXXXXX.XX proc sigbinfrac2dec bin { #puts "Input String to be converted: $bin" if {$bin == 0} { return 0 } elseif {[string match *.* $bin]} { #fractional #set range [string length $bin] set flot [string first . $bin] set sign [string range $bin 0 0] set integer [string range $bin 1 [expr $flot - 1] ] #puts "Integer Part to be converted: $integer" set frac [string range $bin [expr $flot + 1] end] #puts "Fractional Part to be converted: $frac" } else { # No Fractional set sign [string range $bin 0 0] set integer [string range $bin 1 end] } if {[string map [list 1 {} 0 {} . {}] $bin] ne {}} { error "argument is not in base 2 or base 2 fractional: $bin" } #Algo for Integer Part to be executed in both Format set r 0 foreach d [split $integer {}] { incr r $r incr r $d } if {$sign == 1} { set signd -} else { set signd + } if {[string match *.* $bin]} { #Algo for fractional Part to be executed only in case of flotting point being set x 0 set flotd 0 foreach d [split $frac {} ] { set x [incr x -1] set flotd [expr $flotd + [expr {pow(2,$x)} * $d ]] } set flotdo [string range $flotd 1 end] return $signd$r$flotdo } else { return $signd$r } } Expected Output: % source sigbinfrac2dec.tcl % sigbinfrac2dec 11.11 -1.75
# SPEC : Get the 2's complement rappresentation proc complement2 {binary width} { if {[string length $binary] != $width } { puts stdout "Error in complement2 string passed does not match with length declared with width parameter." return -1 } else { set compl [string map {0 1 1 0} $binary] set compl_dec [ bin2dec $compl] set complement_2dec [expr $compl_dec + 1] set complement_2bin [dec2bin_width $complement_2dec $width] return $complement_2bin } }
Expected Output:
complement2 000001100100 12 111110011100