In the past few days, I have been testing various different ways to convert binary numbers to decimal. In the next little script I demonstrate two ways that I designed. One is called Bin2BigInt() which looks slower, and the other is Bin2Dec(). They both do the exact same thing but using a different scheme. You pass a binary number such as "1110101" and the functions output an integer in base 10, which could be any length. Bin2Dec() uses tr/// operator and a regex replace to perform the addition on bigints, while Bin2BigInt() relies on a sub called BIGADD() which adds two bigints digit by digit and returns the sum.
A third scheme could implement a little trick to speed up the conversion by looking for consecutive patches of 1s in the input string... So, let's say we have a number like this: "1111111111110000000000" In this case, we could calculate 2^22 and then subtract 2^10 like so:
10000000000000000000000 = 4194304
- 10000000000 = 1024
---------------------------------------
1111111111110000000000 = 4193280 <= This is the result we're looking
+ for!
The idea is that performing a single subtraction would be faster than performing an addition every time we encounter a '1' digit in the input string. But I'm not sure how much time this would gain. And the gain would be absolutely non-existent for numbers like "1010101011101010101000101010101010101010000000000101010100101010101001101100000101010101" in which there aren't too many repeating 1s.
#!/usr/bin/perl -w
use strict;
use warnings;
$| = 1;
RunTests(
'', '0',
'what?', '0',
'0', '0',
'1', '1',
'11', '3',
'0001', '1',
'1011', '11',
'1111', '15',
'11111111', '255',
'yay 1__1 Lol', '3',
'11111111111111111111111111111111', '4294967295',
'10101010101010101010101010101010', '2863311530',
'00000000001111111111110000000000', '4193280',
'100011111111110011111011001101000', '4831442536',
+ # 33-bit value
'11111111111000111111111100111110110010001000110', '1406773524818
+62', # 47-bit value
'111111111111000000000000000000000000000000000000', '281406257233
+920', # 48-bit value
'1111111111110000000000000000000000000000000000000', '56281251446
+7840', # 49-bit value
'0000111000111000111000111000111000111000111000111000111000111000
+', '1024819115206086200', # 60-bit value
'1111111111111111100000000000000001111111111111111000000000000000
+', '18446603338368647168', # 64-bit value
'1100111100100110000001111110111000110101010101010101010101010101
+', '14926626734644483413', # 64-bit value
'1111111111111111111111111111111111111111111111111111111111111111
+', '18446744073709551615', # 64-bit value
'1000000000000000000000000000000000000000000000000000000000000000
+0', '18446744073709551616', # 65-bit value
# 112-bit value:
'1111111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111', '51922968585348276
+28530496329220095',
'1000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000', '5192296858534827
+628530496329220096',
# 360-bit value:
'1111111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111111111111
+11111111111111111111', '234854258277383322788948059678933702737568254
+8908319870707290971532209025114608443463698998384768703031934975',
# 1500-bit value:
'1000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000', '175373310
+552170193738137939801404289967620079401654144120378990123954819252816
+611018285404432924846308265752033977187586996472744707349798770855194
+590023504239449782426645486322434013557917314732683410921700693147256
+777291324731712626918096946574803223325262758757211677546245866805651
+778980548549427903371569771051088289237163133803665023766376585960668
+373517816863916485209966135263316668342549760000875266777645294402170
+91269193357761841856604274688'
);
print "\nDon't panic. Your computer did not crash.\nThe following oper
+ation may take a few seconds.\n";
print "\nConverting 4096-digit binary number to decimal using Bin2Dec(
+) and Bin2BigInt()\nPlease wait...";
# Generate a 4096-digit binary number:
RunTests('1' . '0' x 4096, '104438888141315250669175271071662438257996
+424904738378038423348328395390797155745684882681193499755834089010671
+443926283798757343818579360726323608785136527794595697654370999834036
+159013438371831442807001185594622637631883939771274567233468434458661
+749680790870580370407128404874011860911446797778359802900668693897688
+178778594690563019026094059957945343282346930302669644305902501597239
+986771421554169383555988529148631823791443449673408781187263949647510
+018904134900841706167509366833385055103297208826955076998361636941193
+301521379682583718809183365675122131849284636812555022599830041234478
+486259567449219461702380650591324561082573183538008760862210283427019
+769820231316901767800667519548507992163641937028537512478401490715913
+545998279051339961155179427110683113409058427288427979155484978295432
+353451706522326906139490598769300212296339568778287894844061600741294
+567491982305057164237715481632138063104590291613692670834285644073044
+789997190178146576347322385026725305989979599609079946920177462481771
+844986745565925017832907047311943316555080756822184657174637329688491
+281952031745700244092661691087414838507841192980452298185733897764810
+312608590300130241346718972667321649151113160292078173803343609024380
+4708340403154190336');
print "\nConverting 8192-digit binary number to decimal using Bin2Dec(
+) and Bin2BigInt()\nPlease wait...";
# Generate a 8192-digit binary number:
RunTests('1' . '0' x 8192, '109074813561941592946298424473378286244826
+416199623269243183278618972133184911929521626423452520198722395729179
+615702527310987082017718406361097976507755479907890629884219298953860
+982522804820515969685161359163819677188654260932456012129055390188630
+101790025253579991720001007960002653583680090529780588095235050163019
+547565391100531236456001484742603529355124584392891875276869627934408
+805561751569434994540667782514081490061610592025643850457801332649356
+583604724240738244281224513151775751916489922636574372243227736807502
+762788304520650179276170094569916849725787968385173704999690096112051
+565505011556127149149251534210574896662954703278632150573082843022166
+497032439613863525162640951616800542762343599630892169144618118740639
+531066540488573943483287742816740749537099351186875635997039011702182
+361674945862096985700626361208270671540815706657513728102702231092756
+491027675916052087830463241104936456875492096732298245918476342738379
+027244843801852697776494107271561158043469082745933999196141424274141
+059911742606055648376375631452761136265862838336862115799363802087853
+767554533678991569423443395566631507008721353547025567031200413072549
+583450835743965382893607708097855057891296790735278005493562156109079
+584517295411597292747987752773856000820411855893000477774872776185381
+351049384058186159865221160596030835640594182118971403786872621948149
+872760365361629885617482241303348543878532402475141941718301228107820
+972930353737280457437209522870362277636394529086980625842235514850757
+103961938744962986680818876966281577815307939317909314364834076173858
+181956300299442279075495506128881830843007964869323217915876591803556
+521615711540299212027615560787310793747746684152836298770869945015203
+123186259420308569383894465706134623670423402682110295895495119708707
+654618662279629453645162075650935101890602377382153953277620867697858
+973196633030889330466516943618507835064156833694453005143749131129883
+436726523859540490427345592872394952522718461740436785475461047437701
+976802557660588103807727070771794222197709038543858584409549211609985
+253890397465570394397308609093059696336076752996493841459818570596375
+456149735582781362383328890630900428801732142480866396267133352800923
+275835087305961411872378142210146019861574738685509689608918918044133
+955852482286754111321263879367556765034036297003193002339782846531854
+723824423202801518968966041882297600081543761065225427016359565087543
+385114712321422726660540358178146909080657646895058766199718650566547
+5715792896');
print "\nNow we will convert 5000 random 128-bit binary numbers using
+Bin2BigInt().\nPress ENTER to begin...";
$a = <STDIN>;
my $DEC;
my $TIME = time;
for (my $count = 0; $count < 5000; $count++)
{
my $random = '';
for (my $bits = 0; $bits < 128; $bits++)
{
$random .= (rand(300) > 150) ? '1' : '0';
}
$DEC = Bin2BigInt($random);
print "\n $random => $DEC";
}
print("\n", time - $TIME, ' secs.');
print "\n\nNow we will convert 5000 random 128-bit binary numbers usin
+g Bin2Dec().\nPress ENTER to begin...";
$a = <STDIN>;
$TIME = time;
for (my $count = 0; $count < 5000; $count++)
{
my $random = '';
for (my $bits = 0; $bits < 128; $bits++)
{
$random .= (rand(300) > 150) ? '1' : '0';
}
$DEC = Bin2Dec($random);
print "\n $random => $DEC";
}
print("\n", time - $TIME, ' secs.');
print "\n\n";
exit;
####################################################################
# RUN TESTS:
#
sub RunTests
{
my $i = 0;
my $ERR = 0;
while ($i < @_)
{
my $THIS_OK = 1;
my $BIN = $_[$i++];
my $CORRECT = $_[$i++];
my $DEC1 = Bin2Dec($BIN);
my $DEC2 = Bin2BigInt($BIN);
if ($CORRECT ne $DEC1)
{
print "\nBin2Dec('$BIN') outputs:\n$DEC1 when it should be:\n$CO
+RRECT\n";
$THIS_OK = 0;
$ERR++;
}
if ($CORRECT ne $DEC2)
{
print "\nBin2BigInt('$BIN') outputs:\n$DEC2 when it should be:\n
+$CORRECT\n";
$THIS_OK = 0;
$ERR++;
}
$THIS_OK and print "\nOK $DEC1";
}
print "\n\n $ERR ERRORS.\n\n";
return !$ERR;
}
####################################################################
#
# This function takes a binary number of any size made up of
# 1s and 0s and returns a decimal number (base 10).
#
# This function can convert a 64-bit or 128-bit binary number to
# a decimal number even when 32-bit processor is used. Regardless
# of processor architecture, it will work on any machine.
#
# The input string can contain any number of digits. Any character
# other than 1s and 0s is going to be ignored. The output number
# is going to be a big integer which may contain hundreds or
# even thousands of digits.
#
# Usage: STRING = Bin2Dec(STRING)
#
sub Bin2Dec
{
defined $_[0] or return 0;
my $B = $_[0];
$B =~ tr|01||cd; # Remove illegal chars
$B =~ s/^0+//; # Remove preceding zeros
(my $L = length($B)) or return 0; # Return 0
$L > 32 or return oct('0b' . $B); # Is it 32 bits or less?
my $DEC = oct('0b' . substr($B, -32)); # Convert last 32 bits
$B = substr($B, 0, -32); # Remove last 32 bits
# Convert number up to 49 bits:
$L < 50 and return $DEC + oct('0b' . $B) * 4294967296;
my $i;
my $N;
my $PWR = "\x06\x09\x02\x07\x06\x09\x04\x09\x02\x04"; # 4294967296
$DEC =~ tr|0-9|\x00-\x09|;
$DEC = reverse($DEC);
$L -= 32;
my $PWR2 = 4294967296;
while ($L-- >= 0)
{
if (chop($B)) # Is the next binary digit a '1' ?
{
# Perform simple addition: $DEC += $PWR
$i = (length($PWR) >> 2) + 2;
while ($i-- > 0)
{
vec($DEC, $i, 32) += vec($PWR, $i, 32);
}
# Perform carry operation:
while ($DEC =~ s/([^\x00-\x09])(.)/ $N = ord($1); pack('CC', cho
+p($N), $N + ord($2)) /esg) {}
$DEC =~ s/([^\x00-\x09])$/ $N = ord($1); pack('CC', chop($N), $N
+) /es;
}
# Here we calculate the next power of two.
# We shift each byte of $PWR to the left by 1.
# The fastest way to do this is using the tr/// operator.
# Each digit 0-9 is represented as an ASCII character
# from \0 to \x09 and so once shifted, the numbers then
# become \x00 to \x12. After this, we perform a carry operation.
# Note: $PWR stores numbers backwards, so "4096" would be
# represented as "\x06\x09\x00\x04".
# Multiply each digit by 2:
$PWR =~ tr|\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0C\x0E\x1
+0\x12\x14\x18\x1C\x20\x24\x28\x30\x38\x40\x48\x70|\x00\x02\x04\x06\x0
+8\x0A\x0C\x0E\x10\x12\x14\x18\x1C\x20\x24\x28\x30\x38\x40\x48\x50\x60
+\x70\x80\x90\xE0|;
# Next, we perform the carry operation again:
while ($PWR =~ s/([^\x00-\x09]{1})(.)/ $N = ord($1); pack('CC', ch
+op($N), $N + ord($2)) /esg) {}
$PWR =~ s/([^\x00-\x09]{1})$/ $N = ord($1); pack('CC', chop($N), $
+N) /es;
}
$DEC =~ tr|\x00-\x09|0-9|;
$DEC =~ s/0+$//;
$DEC = reverse($DEC);
return $DEC;
}
####################################################################
#
# This function converts a binary number from base 2 to base 10
# using BIGADD() function which is slower.
# Accepts any number of digits.
#
# Usage: STRING = Bin2BigInt(STRING)
#
sub Bin2BigInt
{
my $N = defined $_[0] ? $_[0] : '';
$N =~ tr|01||cd; # Remove everything except 1s and 0s
$N =~ s/^0+//; # Remove initial zeros
my $L = length($N);
if ($L == 0) { return '0'; }
if ($L <= 32) { return oct('0b' . $N); }
my $OUTPUT = oct('0b' . substr($N, -32));
my $PWR = 4294967296;
$L -= 32;
while ($L--)
{
if (length($PWR) < 15)
{
if (vec($N, $L, 8) == 49) { $OUTPUT += $PWR; }
$PWR += $PWR;
}
else
{
if (vec($N, $L, 8) == 49) { $OUTPUT = BIGADD($OUTPUT, $PWR); }
$PWR = BIGADD($PWR, $PWR);
}
}
return $OUTPUT;
}
####################################################################
#
# This function adds two big positive integers in base 10 and
# returns the sum. There is no error checking done, so make sure
# to only provide digits in the arguments. Any non-digit character
# will mess up the output.
#
# The 1st and 2nd arguments must contain two big integers that have
# to be added. The 3rd and 4th arguments shift these integers to
# the left or right before the addition. For example:
# BIGADD(4, 5, 1, 0) would shift 4 to the right by 1,
# so it would become 40, and then 40 + 5 = 45.
#
# Usage: BIGINT_SUM = BIGADD(BIGINT_A, BIGINT_B, SHIFT_A, SHIFT_B)
#
sub BIGADD
{
no warnings;
my $A = defined $_[0] ? $_[0] : '';
my $B = defined $_[1] ? $_[1] : '';
$A =~ s/^0//g; $A =~ tr|0-9||cd;
$B =~ s/^0//g; $B =~ tr|0-9||cd;
my $AL = length($A) + int($_[2]);
my $BL = length($B) + int($_[3]);
my $P = ($AL > $BL ? $AL : $BL) + 1;
my $CARRY = 0;
my $SUM = '';
while ($P--)
{
my $DIGIT = (($CARRY >> 1) & 1) +
(vec($A, --$AL, 8) & 15) +
(vec($B, --$BL, 8) & 15);
vec($SUM, $P, 8) = $DIGIT + ($CARRY = ($DIGIT > 9) ? 38 : 48);
}
$SUM =~ s/^0+//; # Discard preceding zeros
return (length($SUM)) ? $SUM : 0;
}