How to compute the factorial of a number greater than 20 in Pascal

1 Answer

0 votes
program ComputeLargeFactorial;

{$mode objfpc}{$H+}{$H-}

{
  Calculating factorials for numbers n > 20 exceeds the capacity of standard 64-bit
  integers (20! is the largest factorial fitting in a 64-bit unsigned integer, UInt64).
  
  To compute exact factorials for arbitrarily large numbers, we implement a custom 
  arbitrary-precision integer (BigInt) dynamic array structure using base 1,000,000,000 
  (10^9). Storing 9 decimal digits per array element optimizes memory usage and 
  leverages modern 64-bit CPUs to perform fast, efficient arithmetic operations 
  with minimal digit carry propagation.
}

uses
  SysUtils;

const
  BASE = 1000000000; // Base 10^9 to store 9 decimal digits per block.

type
  // Dynamic array where element 0 represents the least significant 9 digits.
  TBigInt = array of Int64;

{
  Multiplies an arbitrary-precision integer by a single standard 64-bit integer.
  
  @param Num  The current BigInt value to be multiplied in-place.
  @param Multiplier  The integer value to multiply with.
}
procedure MultiplyBigInt(var Num: TBigInt; Multiplier: Int64);
var
  I: Integer;
  Carry, Product: Int64;
begin
  Carry := 0;
  for I := 0 to High(Num) do
  begin
    Product := Num[I] * Multiplier + Carry;
    Num[I] := Product mod BASE;
    Carry := Product div BASE;
  end;

  // Append remaining carries as new high-order digits.
  while Carry > 0 do
  begin
    SetLength(Num, Length(Num) + 1);
    Num[High(Num)] := Carry mod BASE;
    Carry := Carry div BASE;
  end;
end;

{
  Computes the factorial (n!) of an integer using linear iterative multiplication.
  
  @param N  The input integer.
  @return   The result as a TBigInt structure.
}
function Factorial(N: Integer): TBigInt;
var
  I: Integer;
begin
  // Explicitly initialize the managed result variable to prevent compiler warnings
  Result := nil;

  if N < 0 then
    raise EArgumentException.Create('Factorial is undefined for negative numbers.');

  // Initialize result to 1 (represented as single element array [1])
  SetLength(Result, 1);
  Result[0] := 1;

  for I := 2 to N do
    MultiplyBigInt(Result, I);
end;

{
  Formats a TBigInt value into a human-readable decimal string.
  
  @param Num  The TBigInt to convert.
  @return     The formatted string representation.
}
function BigIntToString(const Num: TBigInt): string;
var
  I: Integer;
  BlockStr: string;
begin
  if Length(Num) = 0 then
    Exit('0');

  // Most significant digit group does not need leading zero padding.
  Result := IntToStr(Num[High(Num)]);

  // Remaining digit groups must be left-padded with zeros to exactly 9 digits.
  for I := High(Num) - 1 downto 0 do
  begin
    BlockStr := IntToStr(Num[I]);
    Result := Result + StringOfChar('0', 9 - Length(BlockStr)) + BlockStr;
  end;
end;

var
  InputVal: Integer;
  FactVal: TBigInt;
begin
  try
    InputVal := 25; // Example calculation for N > 20

    FactVal := Factorial(InputVal);
    
    WriteLn(Format('%d! = %s', [InputVal, BigIntToString(FactVal)]));
    WriteLn(Format('Total Digits: %d', [Length(BigIntToString(FactVal))]));
    
  except
    on E: Exception do
      WriteLn(ErrOutput, 'Error: ', E.Message);
  end;
end.



{
run:

25! = 15511210043330985984000000
Total Digits: 26

}

 



answered Sep 12 by avibootz
...