See Also edit
- Converting numbers from arbitrary bases
- an earlier approach at the same topic
Description edit
Richard Suchenwirth 2002-07-06:Every string standing for a number in a positional value system (as opposed to e.g. Roman numbers or Hebrew numbers) has to be understood relative to its base. We're most familiar with base 10 (the decimal system), where e.g. 123 stands for1*10^2 + 2*10^1 + 3*10^0and in computery the bases 2 (binary), 8 (octal), and 16 (hexadecimal) are also fairly common, but any integer could function as a base as long as we have enough distinct digits. If you want to experiment with other bases, here are two routines that convert a number to and from a different base, up to 62 (base64 uses a very different sequence of digits, so I didn't go that far)Note also that signs are preserved (unlike computery habit to have octals and hexadecimals as unsigned, resp. sign-extended). You could have that with
base $myBase [format %u $number]Also, the C/Tcl markup of prefixing 0 to octals and 0x to hexadecimals is not created or recognized - how would e.g. a number to base 3 be marked? Like in Ada,
3#12021#? So you have to keep track of number bases yourself.
proc base {base number} { set negative [regexp ^-(.+) $number -> number] ;# (1) set digits {0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z} set res {} while {$number} { set digit [expr {$number % $base}] set res [lindex $digits $digit]$res set number [expr {$number / $base}] } if $negative {set res -$res} set res } proc frombase {base number} { set digits {0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z} set negative [regexp ^-(.+) $number -> number] set res 0 foreach digit [split $number ""] { set decimalvalue [lsearch $digits $digit] if {$decimalvalue<0 || $decimalvalue >= $base} { error "bad digit $decimalvalue for base $base" } set res [expr {$res*$base + $decimalvalue}] } if $negative {set res -$res} set res }Examples:
% base 7 1234 3412 % frombase 7 3412 1234 % base 16 255 FFOne might use this to experiment with number palindromes, where the sequence of digits is reverted:
% base 19 42 24 % frombase 19 24 42
# (1): In the Tcl chatroom, Michael Schlenker reported:
set negative [expr {$number != [set number [expr {abs($number)}]]}]is about five times faster if negative, and a bit faster if positive.
A number to base 11 is very frequently used - the check digits in ISBN book numbers, but they use X for 10, instead of A as the code above would do.
MSW responds to: Also, the C/Tcl markup of prefixing 0 to octals and 0x to hexadecimals is not created or recognized - how would e.g. a number to base 3 be marked?
Number['r'|'R'][digits or letters] 0r16 ;# == 0x0 0r1 ;# == 0b0 0R8 ;# == 00 0r10 ;# == 0 0rf ;# == 0x0 0r27That's e.g. how Icon does it, not 100% sure about the 'r', but lisp does it that way, too.escargo 2005-08-22: That's not quite the way it works in Icon. It's really digit-literal radix-specification digit-specification, where radix-specification is r or R. The number specifying the radix comes first:
16rff == 0xff 8r17 == 017 2r11 == 0b11
escargo 2003-08-11: It is worth noting that base does not have to be a positive integer. I did some work (for a high school extra credit assignment) for base -2. These numbers did not have a sign bit in their representations, yet could still represent both positive and negative numbers. The logic for the math operations look funny, but it does work.