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).
See Also edit
- Big bitstring operations
- Converting an integer to a base 2 string
- to.binary
- Math on binary strings
- To little endian
- [two's complement]
Converting Decimal to Binary edit
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 [
1] 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.
Converting Binary to Decimal edit
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
}
##
##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
[Franco Cesari
]# 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
Contributors edit
- RS
- Joseph Collard
- Eric Amundsen
- PYK
- Franco Cesari