harangzsolt33 has asked for the wisdom of the Perl Monks concerning the following question:
# # This function performs simple divisions on big positive integers # in base 10 if ONE of the following three conditions is true: # # * Divisor is 1 digit long (Dividend can be any length) # * Divisor is a multiple of 10 (Dividend can be any length) # * Dividend and divisor are both 15 digits or less # # The 1st argument should be the dividend, the 2nd the divisor. # The 1st return value is the quotient followed by the remainder. # # Usage: LIST = BIGDIVN(BIGINT, DIGIT) # sub BIGDIVN { defined $_[0] && defined $_[1] or return (0, 0); my $A = $_[0]; $A =~ tr|0-9||cd; # Remove illegal chars my $B = $_[1]; $B =~ tr|0-9||cd; # Remove illegal chars $A =~ s/^0+(.+)/$1/; # Remove preceding zeros $B =~ s/^0+(.+)/$1/; # Remove preceding zeros length($A) && $B or return (0, 0); # Divide by zero? $B eq '1' and return ($A, 0); # Divide by one? if ($B =~ m/^1(0+)$/) # Divide by 1, 10, 100, 1000...? { my $Z = length($1); length($A) <= $Z and return (0, $A); my $Q = substr($A, 0, length($A) - $Z); my $R = substr($A, -$Z); $Q =~ s/^0+(.+)/$1/; # Remove preceding zeros from quotient $R =~ s/^0+(.+)/$1/; # Remove preceding zeros from remainder return ($Q, $R); } elsif (length($A) < 16 && length($B) < 16) # Divide small numbers { return (int($A / $B), $A % $B); } elsif (length($B) != 1) { return (0, 0); } # We only accept simple divisons # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + # Okay, here we're going to divide a big integer by a single digit. # First, we create lookup tables for processing big integers. # These lookup tables are necessary, so we can completely # avoid doing divisions or multiplications. # The first lookup table ($LUT) is for finding the quotient. # When dividing any 1-digit or 2-digit number by the divisor, # the result is going to be found in $LUT. # The second lookup table ($MUL) is a multiplication table # in which each byte is a product of the divisor going # from 0 to 9. So, if we're dividing by 6, then $MUL is going to # hold the following: "\0\6\x0B\x12\x1B\x1E\x24\x2A\x30\x36" my $S = 0; # Holds the previous multiplication's result my $i = -1; # Counter goes from 0 to 9 my $LUT = ''; # Lookup table #1 my $MUL = ''; # Lookup table #2 while (++$i < 10) { $MUL .= chr($S); # Build lookup tables... $LUT .= chr($i) x $B; $S += $B; } $A =~ tr|0-9|\x00-\x09|; # Subtract 48 from dividend's digits my $OUTPUT = ''; # This will hold the quotient my $REM = 0; # This will hold the remainder my $Q; # Current digit of the quotient $i = -1; # Counter used to read digits from dividend while (++$i < length($A)) { $S = $REM . vec($A, $i, 8); # Merge remainder with current digit $Q = vec($LUT, $S, 8); # Calculate next digit for quotient $REM = $S - vec($MUL, $Q, 8); # Calculate remainder $OUTPUT .= $Q; } $OUTPUT =~ s/^0+(.+)/$1/; # Remove preceding zeros from quotient return ($OUTPUT, $REM); } sub __Test_BIGDIVN { return RunTest( 'BIGDIVN(0, 0)', '0 0', 'BIGDIVN(0, 10000)', '0 0', 'BIGDIVN(2179014949932, 3)', '726338316644 0', 'BIGDIVN(2179014949933, 3)', '726338316644 1', 'BIGDIVN(2179014949934, 3)', '726338316644 2', 'BIGDIVN(2179014949935, 3)', '726338316645 0', 'BIGDIVN(2179014949936, 3)', '726338316645 1', 'BIGDIVN(2179014949930, 5)', '435802989986 0', 'BIGDIVN(2179014949931, 5)', '435802989986 1', 'BIGDIVN(2179014949932, 5)', '435802989986 2', 'BIGDIVN(2179014949933, 5)', '435802989986 3', 'BIGDIVN(2179014949934, 5)', '435802989986 4', 'BIGDIVN(2179014949935, 5)', '435802989987 0', 'BIGDIVN(2179014949936, 5)', '435802989987 1', 'BIGDIVN("591092491992919449491", "0")', '0 0', 'BIGDIVN("18474690051828881094465", 3)', '6158230017276293698155 0' +, 'BIGDIVN("18474690051828881094466", 3)', '6158230017276293698155 1' +, 'BIGDIVN("18474690051828881094467", 3)', '6158230017276293698155 2' +, 'BIGDIVN("18474690051828881094468", 3)', '6158230017276293698156 0' +, 'BIGDIVN("18474690051828881094469", 3)', '6158230017276293698156 1' +, 'BIGDIVN("18474690051828881094470", 3)', '6158230017276293698156 2' +, 'BIGDIVN("18474690051828881094465", 5)', '3694938010365776218893 0' +, 'BIGDIVN("18474690051828881094466", 5)', '3694938010365776218893 1' +, 'BIGDIVN("18474690051828881094467", 5)', '3694938010365776218893 2' +, 'BIGDIVN("18474690051828881094468", 5)', '3694938010365776218893 3' +, 'BIGDIVN("59109249199291944949881", 8)', '7388656149911493118735 1' +, 'BIGDIVN("59109249199291944949881", "1")', + '59109249199291944949881 0', 'BIGDIVN("59109249199291944949881", "100000000000000000")', + '591092 49199291944949881', 'BIGDIVN("59109249199291944949881", "10000000000000000000000000")', + '0 59109249199291944949881', 'BIGDIVN("50000000000000000000000", "1000000000000000")', + '50000000 0', ); } sub RunTest { @_ or return 1; my $TESTS = @_ >> 1; my $ERRORS = 0; my $PTR = 0; my $SUBNAME = ($_[0] =~ m/([a-zA-Z0-9]+)\(/) ? $1 : ''; for (my $i = 1; $i <= $TESTS; $i++) { my $CODE = $_[$PTR++]; my $CORRECT = $_[$PTR++]; my @RETURN = eval($CODE); my $RETCOUNT = @RETURN; foreach (@RETURN) { defined $_ or $_ = '<undef>'; } my $OUTPUT = join(' ', @RETURN); if ($OUTPUT ne $CORRECT) { $ERRORS++; print "\n", '-' x 78, "\nERROR #$ERRORS IN TEST $i: $CODE => |$OUTPUT| RetCount: +$RETCOUNT", "\n\n Correct: |$CORRECT|\n"; } } print( ($ERRORS) ? "\nFAIL - $SUBNAME() with $ERRORS errors." : "\nO +K - $SUBNAME()"); return ($ERRORS) ? 0 : 1; } __Test_BIGDIVN();
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Division of big integers
by LanX (Saint) on Dec 31, 2024 at 16:31 UTC | |
by NERDVANA (Priest) on Dec 31, 2024 at 21:47 UTC | |
by karlgoethebier (Abbot) on Jan 01, 2025 at 00:04 UTC | |
by LanX (Saint) on Jan 01, 2025 at 02:34 UTC | |
by etj (Priest) on Jan 01, 2025 at 04:10 UTC | |
by LanX (Saint) on Dec 31, 2024 at 22:31 UTC | |
by harangzsolt33 (Deacon) on Jan 01, 2025 at 04:44 UTC | |
by NERDVANA (Priest) on Jan 01, 2025 at 16:54 UTC | |
by soonix (Chancellor) on Jan 01, 2025 at 12:48 UTC | |
by LanX (Saint) on Jan 01, 2025 at 14:01 UTC | |
by harangzsolt33 (Deacon) on Jan 01, 2025 at 04:29 UTC | |
by LanX (Saint) on Jan 01, 2025 at 11:13 UTC | |
|
Re: Division of big integers
by tybalt89 (Monsignor) on Dec 31, 2024 at 20:44 UTC | |
by tybalt89 (Monsignor) on Dec 31, 2024 at 22:28 UTC | |
|
Re: Division of big integers
by Danny (Chaplain) on Jan 07, 2025 at 00:23 UTC |