Программа рекурсивной факториальной функции возвращает 0 для ввода большого числа в C - PullRequest
5 голосов
/ 03 февраля 2020

Я написал программу с использованием рекурсии для факторных вычислений, как преподавали в классе. Я наблюдал это: факториал с числом больше 65 последовательно выводит как 0.

//program to recursively calculate the value of factorial for a given integer!

#include <stdio.h>
#include <stdint.h>

unsigned long rfacta(uint32_t n){
    if(n>0)
        return rfacta(n-1)*n;
    else
        return 1;
}

unsigned long rfactd(uint32_t n){
    if(n>0)
        return n*rfactd(n-1);
    else
        return 1;
}

int main(void){
    uint32_t number, methd;
    unsigned long fctorial;
    printf(" Enter 0 for call-time recursion\n Enter 1 for return-time recursion\n Enter 2 to print the table of passible factorials on this system\n\n");
    scanf("%d",&methd);
    switch(methd){
    case 0:
        printf("Compute factorial for number = ? \n");
        scanf("%d",&number);
        fctorial = rfacta(number);
        printf("Factorial(%d) = %lu\n",number,fctorial);
    break;

    case 1:
        printf("Compute factorial for number = ? \n");
        scanf("%d",&number);
        fctorial = rfactd(number);
        printf("Factorial(%d) = %lu\n",number,fctorial);
    break;

    case 2:
      for(int i=0;rfacta(i)!=0;i++){
        printf("Factorial(%d) = %lu\n",i,rfacta(i));
      }
      break;

    default:
      printf("Incorrect entry! \n");
      break; 
    }
    return 0;
}

output Приложение скриншота: Программа рекурсии для факториала, возвращающего 0

Вопрос:

Как мы видим на выходе [прикрепленное изображение скриншота], есть три области:

Область 1: В этой области выходные данные для факториала верны. Причина этого заключается в том, что

log2 (20!) ~ = 62, [здесь logm (n) означает логарифм n для базы m], то есть предполагается, что 64-битная машина, то наибольшее факториальное число то, что может быть правильно представлено на одном адресе, составляет 20 !, и после чего происходит переполнение памяти, потому что log2 (21!) ~ = 65. Следовательно, такое большое число не может быть представлено 64-битным адресом.

Регион 2: В этом регионе значение факториала ненулевое, но неверное. Причина этого, как описано выше, заключается в том, что целое число факториала настолько велико, что его нельзя представить одним 64-битным адресом. Таким образом, то, что мы вместо этого видим в качестве выходных данных функции, на самом деле представляет собой случайные целые числа без знака длиной 64 бита.

Область 3: для факториала (n> 65), факториал (n) = 0.

Я не могу понять, почему именно для n> 65, n! = 0, как возвращается программой?

Из того, что я понимаю, область 2 должна go включаться, а не останавливаться. Но это постоянно останавливалось на всех машинах, которые я пробовал в своем коде при n = 65, меня озадачило. Заранее спасибо за помощь.

Ответы [ 2 ]

8 голосов
/ 03 февраля 2020

Если вы печатаете значения в шестнадцатеричном формате, происходящее становится более очевидным:

Factorial(0) = 0000000000000001
Factorial(1) = 0000000000000001
Factorial(2) = 0000000000000002
Factorial(3) = 0000000000000006
Factorial(4) = 0000000000000018
Factorial(5) = 0000000000000078
Factorial(6) = 00000000000002d0
Factorial(7) = 00000000000013b0
Factorial(8) = 0000000000009d80
Factorial(9) = 0000000000058980
Factorial(10) = 0000000000375f00
Factorial(11) = 0000000002611500
Factorial(12) = 000000001c8cfc00
Factorial(13) = 000000017328cc00
Factorial(14) = 000000144c3b2800
Factorial(15) = 0000013077775800
Factorial(16) = 0000130777758000
Factorial(17) = 0001437eeecd8000
Factorial(18) = 0016beecca730000
Factorial(19) = 01b02b9306890000
Factorial(20) = 21c3677c82b40000
Factorial(21) = c5077d36b8c40000
Factorial(22) = eea4c2b3e0d80000
Factorial(23) = 70cd7e2933680000
Factorial(24) = 9343d3dcd1c00000
Factorial(25) = 619fb0907bc00000
Factorial(26) = ea37eeac91800000
Factorial(27) = b3e62c3358800000
Factorial(28) = ad2cd59dae000000
Factorial(29) = 9e1432dcb6000000
Factorial(30) = 865df5dd54000000
Factorial(31) = 4560c5cd2c000000
Factorial(32) = ac18b9a580000000
Factorial(33) = 2f2fee5580000000
Factorial(34) = 445da75b00000000
Factorial(35) = 58cde17100000000
Factorial(36) = 7cf3b3e400000000
Factorial(37) = 0f38fff400000000
Factorial(38) = 4275fe3800000000
Factorial(39) = 1ff9ba8800000000
Factorial(40) = ff05254000000000
Factorial(41) = d7d2f74000000000
Factorial(42) = 689c908000000000
Factorial(43) = 924c458000000000
Factorial(44) = 251bf20000000000
Factorial(45) = 85e98a0000000000
Factorial(46) = 0ff6cc0000000000
Factorial(47) = ee4f740000000000
Factorial(48) = aee5c00000000000
Factorial(49) = 79f9c00000000000
Factorial(50) = d2c7800000000000
Factorial(51) = fdbe800000000000
Factorial(52) = 8ab2000000000000
Factorial(53) = b6da000000000000
Factorial(54) = 91fc000000000000
Factorial(55) = 5d24000000000000
Factorial(56) = 5fe0000000000000
Factorial(57) = 58e0000000000000
Factorial(58) = 22c0000000000000
Factorial(59) = 0240000000000000
Factorial(60) = 8700000000000000
Factorial(61) = 2b00000000000000
Factorial(62) = 6a00000000000000
Factorial(63) = 1600000000000000
Factorial(64) = 8000000000000000
Factorial(65) = 8000000000000000

По мере того, как вы продолжаете умножаться на значения, содержащие в качестве коэффициента 2, число конечных нулей продолжает увеличиваться. Ко времени умножения на 66 все ненулевые биты будут вытеснены влево, поэтому у вас останется 0.

Кроме того, значения из 21! до 65! на самом деле это не случайные значения, а младшие 64 бита результата. Целочисленное арифметическое без знака c выполняется по модулю 2 bitlen , где "bitlen" - длина в битах рассматриваемого типа, которая в данном случае равна 64.

7 голосов
/ 03 февраля 2020

Пока вы умножаете на два дополнения и переноса (что не гарантируется стандартом C, но, кажется, происходит в вашей программе), вычисленный результат является полным математическим результатом (без переноса) по модулю 2 64 .

Для n > 65, n ! кратно 2 64 (факторы от 1 до 66 включали 64 или более двойок), поэтому значение по модулю 2 64 равно нулю:

  • При умножении на 2 предоставляется один коэффициент два.
  • При умножении на 4 предоставляются два фактора.
  • При умножении на 6 предоставляется один коэффициент.
  • Когда вы умножаете на 8, предоставляется три фактора.
  • Общее число факторов в n ! is n / 2 + n / 4 + n / 8 +…, используя целочисленное арифметическое c (то есть, обрезая деление, поэтому 3 / 2 = 1) и продолжается до тех пор, пока член не достигнет нуля. Для 66 !, это 66/2 + 66/4 + 66/8 + 66/16 + 66/32 + 66/64 = 33 + 16 + 8 + 4 + 2 + 1 = 64.
...