Arbitrary-precision Arithmetic - Example

Example

The calculation of factorials can easily produce very large numbers. This is not a problem for their usage in many formulae (such as Taylor series) because they appear along with other terms, so that—given careful attention to the order of evaluation—intermediate calculation values are not troublesome. If approximate values of factorial numbers are desired, Stirling's approximation gives good results using floating-point arithmetic. The largest representable value for a fixed-size integer variable may be exceeded even for relatively small arguments as shown in the table below. Even floating-point numbers are soon outranged, so it may help to recast the calculations in terms of the logarithm of the number.

But if exact values for large factorials are desired, then special software is required, as in the pseudocode that follows, which implements the classic algorithm to calculate 1, 1×2, 1×2×3, 1×2×3×4, etc. the successive factorial numbers.

Constant Limit = 1000; %Sufficient digits. Constant Base = 10; %The base of the simulated arithmetic. Constant FactorialLimit = 365; %Target number to solve, 365! Array digit of integer; %The big number. Integer carry,d; %Assistants during multiplication. Integer last,i; %Indices to the big number's digits. Array text of character;%Scratchpad for the output. Constant tdigit of character = ; BEGIN digit:=0; %Clear the whole array. digit:=1; %The big number starts with 1, last:=1; %Its highest-order digit is number 1. for n:=1 to FactorialLimit do %Step through producing 1!, 2!, 3!, 4!, etc. carry:=0; %Start a multiply by n. for i:=1 to last do %Step along every digit. d:=digit*n + carry; %The classic multiply. digit:=d mod Base; %The low-order digit of the result. carry:=d div Base; %The carry to the next digit. next i; while carry > 0 %Store the carry in the big number. if last >= Limit then croak("Overflow!"); %If possible! last:=last + 1; %One more digit. digit:=carry mod Base; %Placed. carry:=carry div Base; %The carry reduced. Wend %With n > Base, maybe > 1 digit extra. text:=" "; %Now prepare the output. for i:=1 to last do %Translate from binary to text. text:=tdigit]; %Reversing the order. next i; %Arabic numerals put the low order last. Print text," = ",n,"!"; %Print the result! next n; %On to the next factorial up. END;

With the example in view, a number of details can be discussed. The most important is the choice of the representation of the big number. In this case, only integer values are required for digits, so an array of fixed-width integers is adequate. It is convenient to have successive elements of the array represent higher powers of the base.

The second most important decision is in the choice of the base of arithmetic, here ten. There are many considerations. The scratchpad variable d must be able to hold the result of a single-digit multiply plus the carry from the prior digit's multiply. In base ten, a sixteen-bit integer is certainly adequate as it allows up to 32767. However, this example cheats, in that the value of n is not itself limited to a single digit. This has the consequence that the method will fail for n > 3200 or so. In a more general implementation, n would also use a multi-digit representation. A second consequence of the shortcut is that after the multi-digit multiply has been completed, the last value of carry may need to be carried into multiple higher-order digits, not just one.

There is also the issue of printing the result in base ten, for human consideration. Because the base is already ten, the result could be shown simply by printing the successive digits of array digit, but they would appear with the highest-order digit last (so that 123 would appear as "321"). The whole array could be printed in reverse order, but that would present the number with leading zeroes ("00000...000123") which may not be appreciated, so we decided to build the representation in a space-padded text variable and then print that. The first few results (with spacing every fifth digit and annotation added here) are:

Reach of computer integers. 1 = 1! 2 = 2! 6 = 3! 24 = 4! 120 = 5! 8-bit 255 720 = 6! 5040 = 7! 40320 = 8! 16-bit 65535 3 62880 = 9! 36 28800 = 10! 399 16800 = 11! 4790 01600 = 12! 32-bit 42949 67295 62270 20800 = 13! 8 71782 91200 = 14! 130 76743 68000 = 15! 2092 27898 88000 = 16! 35568 74280 96000 = 17! 6 40237 37057 28000 = 18! 121 64510 04088 32000 = 19! 2432 90200 81766 40000 = 20! 64-bit 18446 74407 37095 51615 51090 94217 17094 40000 = 21! 11 24000 72777 76076 80000 = 22! 258 52016 73888 49766 40000 = 23! 6204 48401 73323 94393 60000 = 24! 1 55112 10043 33098 59840 00000 = 25! 40 32914 61126 60563 55840 00000 = 26! 1088 88694 50418 35216 07680 00000 = 27! 30488 83446 11713 86050 15040 00000 = 28! 8 84176 19937 39701 95454 36160 00000 = 29! 265 25285 98121 91058 63630 84800 00000 = 30! 8222 83865 41779 22817 72556 28800 00000 = 31! 2 63130 83693 36935 30167 21801 21600 00000 = 32! 86 83317 61881 18864 95518 19440 12800 00000 = 33! 2952 32799 03960 41408 47618 60964 35200 00000 = 34! 128-bit 3402 82366 92093 84634 63374 60743 17682 11455 1 03331 47966 38614 49296 66651 33752 32000 00000 = 35!

We could try to use the available arithmetic of the computer more efficiently. A simple escalation would be to use base 100 (with corresponding changes to the translation process for output), or, with sufficiently wide computer variables (such as 32-bit integers) we could use larger bases, such as 10,000. Working in a power-of-2 base closer to the computer's built-in integer operations offers advantages, although conversion to a decimal base for output becomes more difficult. On typical modern computers, additions and multiplications take constant time independent of the values of the operands (so long as the operands fit in single machine words), so there are large gains in packing as much of a bignumber as possible into each element of the digit array. The computer may also offer facilities for splitting a product into a digit and carry without requiring the two operations of mod and div as in the example, and nearly all arithmetic units provide a carry flag which can be exploited in multiple-precision addition and subtraction. This sort of detail is the grist of machine-code programmers, and a suitable assembly-language bignumber routine can run much faster than the result of the compilation of a high-level language, which does not provide access to such facilities.

For a single-digit multiply the working variables must be able to hold the value (base-1)² + carry, where the maximum value of the carry is (base-1). Similarly, the variables used to index the digit array are themselves limited in width. A simple way to extend the indices would be to deal with the bignumber's digits in blocks of some convenient size so that the addressing would be via (block i, digit j) where i and j would be small integers, or, one could escalate to employing bignumber techniques for the indexing variables. Ultimately, machine storage capacity and execution time impose limits on the problem size.

Read more about this topic:  Arbitrary-precision Arithmetic

Famous quotes containing the word example:

    Our intellect is not the most subtle, the most powerful, the most appropriate, instrument for revealing the truth. It is life that, little by little, example by example, permits us to see that what is most important to our heart, or to our mind, is learned not by reasoning but through other agencies. Then it is that the intellect, observing their superiority, abdicates its control to them upon reasoned grounds and agrees to become their collaborator and lackey.
    Marcel Proust (1871–1922)