''Problem:'' how to convert numbers between their decimal and binary representation. I.e., we are looking for a pair of procs {int2bits, bits2int} such that * int2bits 6 -> {1 1 0} * bits2int {1 1 0} -> 6 ---- TS Caveat, negative int are not always carefully handled (while {$i>0}...). ---- [Richard Suchenwirth] suggested in c.l.t. proc int2bits {i} { #returns a bitslist, e.g. int2bits 10 => {1 0 1 0} set res "" while {$i>0} { set res [expr {$i%2}]$res set i [expr {$i/2}] } if {$res==""} {set res 0} split $res "" } proc bits2int {bits} { #returns integer equivalent of a bitlist set res 0 foreach i $bits { set res [expr {$res*2+$i}] } set res } ---- Joseph Collard added optional field width: proc int2bits {i {digits {} } } { #returns a bitslist, e.g. int2bits 10 => {1 0 1 0} # digits determines the length of the returned list (left truncated or added left 0 ) # use of digits allows concatenation of bits sub-fields set res "" while {$i>0} { set res [expr {$i%2}]$res set i [expr {$i/2}] } if {$res==""} {set res 0} if {$digits != {} } { append d [string repeat 0 $digits ] $res set res [string range $d [string length $res ] end ] } split $res "" } ---- [MS]: Another approach is using the [binary] command: proc int2bits {i} { #returns a bitslist, e.g. int2bits 10 => {1 0 1 0} binary scan [binary format I1 $i] B* x split [string trimleft $x 0] {} } proc bits2int {bits} { #returns integer equivalent of a bitlist set bits [format %032s [join $bits {}]] binary scan [binary format B* $bits] I1 x set x } [Cameron Laird] rightly noted the platform dependence of the "%032s" part. To correct that, do instead proc int2bits {i} { #returns a bitslist, e.g. int2bits 10 => {1 0 1 0} binary scan [binary format I1 $i] B* x split [string trimleft $x 0] {} } set tmp [llength [int2bits -1]] regsub @ { #returns integer equivalent of a bitlist set bits [format %0@s [join $bits {}]] binary scan [binary format B* $bits] I1 x set x } $tmp tmp proc bits2int {bits} $tmp unset tmp '''Note''': I'm leaving this for now, as an illustration. But it is *not* correct, the 32-bit dependence is still there, implicit in the "I" format specification. A correct approach needs to wait until there are 64 bit format specs for [binary] ---- 'Nother variation: use [format] to convert to octal (or hexadecimal, for that matter), and "[string] map ..." to transform octal digits to bit patterns. proc int2bits x { string map { 0 {0 0 0} 1 {0 0 1} 2 {0 1 0} 3 {0 1 1} 4 {1 0 0} 5 {1 0 1} 6 {1 1 0} 7 {1 1 1} } [split [format %o $x] ""] } ;#RS - note however that the bit sequence is multiples of 3 long: % int2bits 9 0 0 1 0 0 1 % int2bits 255 0 1 1 1 1 1 1 1 1 [Lars H]: A simple variation on that, without leading zeros and about 30% faster, since it avoids the [split]: proc int2bits x { string map { +0 0 +1 1 +2 {1 0} +3 {1 1} +4 {1 0 0} +5 {1 0 1} +6 {1 1 0} +7 {1 1 1} 0 { 0 0 0} 1 { 0 0 1} 2 { 0 1 0} 3 { 0 1 1} 4 { 1 0 0} 5 { 1 0 1} 6 { 1 1 0} 7 { 1 1 1} } [format +%o $x] } ---- Helmut Giese in c.l.t: proc val2Bin val { set binRep [binary format c $val] binary scan $binRep B* binStr return $binStr } # some tests puts [val2Bin 10] puts [val2Bin 129] puts [val2Bin 0x7F] ---- [Arjen Markus] The following fragment converts a hexadecimal representation to a floating-point number: # Convert between float and hex # set hex "f3080000" set bin [binary format h8 $hex] binary scan $bin f float puts "$float" On a big-endian machine the result is "1.0" (on a little-endian machine it is bizarre: 4.6006..e-041, but that is simply because the bytes are reversed.) ---- For extracting "unsigned" floating-point values from very long (>32 bits) integers in hex, [MS] has this recommendation on c.l.t.: proc conv {largeHex} { set res 0.0 foreach hexDigit [split $largeHex {}] { set new 0x$hexDigit set res [expr {16.0*$res + $new}] } return $res } ---- [Arjen Markus] Suppose you have a sequence of bytes that are read from a file. Two of these, say at position 6 and 7 (counting from 0), make up an integer number. Then: set two_bytes [string range $str 6 7] binary scan "\0\0$two_bytes" I intvalue will turn these two bytes into an integer (consisting of 4 bytes, hence the two leading nulls), if the original data are in big-endian order. If they are in little-endian order, use: set two_bytes [string range $str 6 7] binary scan "${two_bytes}\0\0" i intvalue (Note: trailing nulls and a different format) ---- [Eric Amundsen] - Here's a solution that seems to work fine for 64-bit 2's complement integer math. proc bin2int {binString} { set result 0 for {set j 0} {$j < [string length $binString]} {incr j} { set bit [string range $binString $j $j] set result [expr $result << 1] set result [expr $result | $bit] } return $result } set binaryString 11001 puts [bin2int $binaryString] ---- [KBK] contributed this version in the [Tcl chatroom] on 2002-11-18: proc fromBinary { digitString } { set r 0 foreach d [split $digitString {}] { incr r $r incr r $d } return $r } ---- [TV] (24-4-'03) The above is along the lines which one could use in a reusable low level implementation. The complement in similar manner could be: set a 16; set r {}; while {$a != 0} {set h [expr $a/2]; if {[expr $h+$h] != $a} {set r "1$r"} {set r "0$r"} ; set a $h } puts $r The result should be split to get a list from the string, and the domain is limited by the ceiling for expr based addition and division. Addition and division by two can be fast for low level implementation, and the method can probably easily be used with an infinite math package (I've seen one but didn't try it out yet). ---- [Arts and crafts of Tcl-Tk programming] - [Category Mathematics]