4

I am not able to find out why my function returns the user input only rather then the factorial of the input.

#include <stdio.h>
#include <math.h>
int factorial(int x)
{
    //int x;
    int sum = 1;
    while (x!=0){
        sum = sum * x;
        x--;
    }
    return sum;
}

int main(){
    int x;
    printf("Enter value of x: ");
    scanf("%i",&x);
    factorial(x);
    printf("sum is %i", x);
    
    return 0;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
mitday
  • 43
  • 5
  • 7
    Answers below. Just to note factorial isn't a `sum`. It's a product. Your variable would be so much better called `prod` or even `product`. – Persixty Sep 26 '22 at 14:46
  • 1
    I'm curious about why you kept the `//int x;` comment in the code you showed. Uncommenting it would be wrong — you need to use the parameter `x`. So why keep that comment at all? – Jonathan Leffler Sep 26 '22 at 14:47
  • 2
    Note the limited range of factorials: • A 32-bit integer can store factorials up to 12! • A 64-bit integer can store factorials up to 20! • A 128-bit (unsigned) integer can store factorials up to 34! • A 256-bit integer can store factorials up to 57! • A 512-bit (unsigned) integer can store factorials up to 98! • Using IEEE 754 64-bit floating-point arithmetic, you can store approximations up to 170! (7.257415615307994E+306) —— You probably don't have a compiler that handles sizes bigger than 64-bit integers (though GCC has rudimentary support for 128-bit integers). – Jonathan Leffler Sep 26 '22 at 14:50
  • 2
    I'm also curious why you have `#include ` in a program that only uses integer arithmetic. At one level, there's no harm done — nothing breaks because you include that header. But it's a good idea to keep the list of headers minimal. – Jonathan Leffler Sep 26 '22 at 14:54
  • 1
    IMHO, better to use the condition `while(x>0)` rather than `while(x!=0)`. You currently do no validation on user input.. what if I enter -3, for example? Your loop will continue to crank, quickly overflowing `sum`, creating UB and returning a bogus value. `while(x>0)` eliminates at least that potential problem. – yano Sep 26 '22 at 15:11

6 Answers6

6

Your factorial function does return a new value, but then you don't actually use that value.

printf("sum is %i\n", factorial(x));
Chris
  • 26,361
  • 5
  • 21
  • 42
5

Because you are printing x which is the variable that you have stored the user input in. Your factorial function returns the result, but you are not saving it.

chameleon
  • 136
  • 1
  • 7
2

I think the variable names were not proper and you printed x instead of printing factorial.

#include <stdio.h>
int factorial(int x)
{
int fact = 1;
while (x!=0){
    fact = fact * x;
    x--;
}
return fact;
}

int main(){
   int x;
   printf("Enter value of x: ");
   scanf("%i",&x);
   printf("Factorial is %i",factorial(x));
   return 0;
}
Ajay Pun Magar
  • 425
  • 2
  • 13
1

For starters the function as is

int factorial(int x)
{
    //int x;
    int sum = 1;
    while (x!=0){
        sum = sum * x;
        x--;
    }
    return sum;
}

can invoke undefined behavior if the user will pass a negative value, and the function accepts negative values.

The function argument should have an unsigned integer type instead of the signed integer type int

For non-negative values the maximum value of the types int or unsigned int for which the factorial can be calculated is equal to 12.

So to be able to calculate the factorial for greater values you should use the type unsigned long long int. In this case the maximum value for which the factorial can be calculated correctly is equal to 20.

The function can look the following way

unsigned long long int factorial( unsigned long long int x )
{
    unsigned long long int product = 1;

    for ( ; 1 < x; --x )
    {
        product *= x;
    }

    return product;
}

In your program you are not using the returned value of the function.

factorial(x);

The function main can look the following way

int main( void )
{
    unsigned int x;

    printf( "Enter value of x: " );

    if ( scanf( "%u",&x ) == 1 )
    {
        printf("The factorial of %u is equal to %llu\n, x, factorial( x ) );
    }
    
    return 0;
}

Now try the program by entering the value for x equal to 20 and see the program output.:)

You could check in the if statement that the user did not enter a value greater than 20 as for example

    if ( scanf( "%u",&x ) == 1 && !( 20 < x ) )

Though it would be better if the function itself will check the value of the passed argument.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • re. argument should have unsigned type, I'm not really a fan of using signedness to convey whether or not negative values are valid, and the undefined behaviour of under-/overflow is often replaced by defined behaviour that is unwanted and harder to detect programmatically. There are of course arguments either way, but mine is that the default should be signed and restrictions on the range should be documented as preconditions. (Similarly I wouldn't suggest using something like `uint8_t` just to document limited range of positive numbers whose factorial fits in the return type.) – Arkku Sep 26 '22 at 15:31
  • 1
    (On a related note, the largest factorial that fits in a 64-bit integer is 20! so the _argument_ type being `unsigned long long` doesn't make any sense.) – Arkku Sep 26 '22 at 15:35
  • @Arkku You are mistaken. You always shall write more general functions that accept argument of most types. – Vlad from Moscow Sep 26 '22 at 15:40
  • @Arkku The argument shall not be signed. The function declaration is a contract between the function and the caller. Otherwise you only confuse readers of the function. – Vlad from Moscow Sep 26 '22 at 15:42
  • 2
    That is one opinion, and like I said, there are arguments either way. Popular style guides (e.g., Google's) disagree with you, as do I, and it's ok to disagree, but I don't think stating this as though it is a universal fact is a good idea. Still, of course you can write your answer (which is otherwise good!) as you wish, I just wanted to say the differing opinion for future readers. Related article: https://hamstergene.github.io/posts/2021-10-30-do-not-use-unsigned-for-nonnegativity/ – Arkku Sep 26 '22 at 15:47
  • re. "function declaration is a contract" – more precisely, the function declaration is _part of_ the contract. There are many things that you cannot put into the declaration (in C, some of them you can in other languages like Ada or Eiffel). For example, the range of valid numbers, format of data, null termination of arrays, etc. Yet all of these are the part of the contract. Likewise the return value is part of the contract, here that it is the factorial of the argument, yet that has to be documented by the name of the function. – Arkku Sep 26 '22 at 15:57
0

Failure to use function return value

As others have said:

//factorial(x);
//printf("sum is %i", x);
printf("sum is %i", factorial(x));

To improve factorial()

  • Since factorial is a product, change the sum name.

  • Cope with pathologic inputs like values less than 0 or very large. Code code exit, but maybe instead return a error value. Determination of the upper limit could be done beforehand (as below), at run time or with makefile magic.

  • Use a wider type to handle large values. Maybe even use an unsigned type. uintmax_t has the greatest max value. It is at least 64 bits.

Example

#include <limits.h>
#include <stdint.h>

#if UINTMAX_MAX == 0xFFFFFFFFFFFFFFFFu
  #define FACTORIAL_MAX 20
#elif (UINTMAX_MAX >> 64) == 0xFFFFFFFFFFFFFFFFu
  #define FACTORIAL_MAX 34
#else
  #error TBD FACTORIAL_MAX
#endif

// Compute factorial.
// Return 0 on error.
uintmax_t factorial(int x) {
  if (x < 0 || x > FACTORIAL_MAX) {
    return 0;
  }
  uintmax_t product = 1;
  while (x > 0) {
    product = product * x;
    x--;
  }
  return product;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

For very large factorials:

DO NOT multiply integers, or even floating point numbers. Compute the logarithms of the numbers, and add the logarithms.

For example, if you are computing the probability of the values of a binomial probability distribution (say for flipping a coin 1000 times), you will need to compute 1000 factorial and divide it by the factorials of the number of heads and the factorial of the number of tails.

1000 factorial would have 2570 decimal digits, with the first digit a "1" and the last 200 digits being "0", with a dog's breakfast of digits in between. Approximated as a floating point decimal number, 1000! is 1.8313956e2570.

But if you are calculating many numbers, for example the table of probabilities for a flipped coin, it is far easier to add logarithms, then convert with exp(), rather than multiplying up to thousand digit numbers.

You can find libraries for "bigint" math, but for solving practical problems (Is the coin biased, and how certain can I be of that? At 6 sigma computed bias, I shoot the crooked coin owner), why bother?

The natural logarithm of 1000 factorial is less than 6000; you can probably compute a million factorial this way. Using a 64 bit float, the enormous exponent for 1M! will eat up all your significant digits, but "bigfloat" math will give you adequate accuracy for calculations.

That said, a million coin flips and subsequent calculations will age the participants to death.

For practical calculations like the binomial formula, you end up dividing by two "half-largish" factorials and a heap of 2s. So, you can schedule some "divides" (pairing/cancelling terms and subtracting some logarithms from the total) through the course of the computation, to keep the sum small and the floating point accuracy good.

I'm sure that Real Numerical Mathematicians have many accuracy-preserving tricks for calculations like this, simple ways to pair and cancel factors in the fraction's numerator and denominator so that you are only calculating what's necessary, and not growing floating point number logarithms into the reduced mantissa accuracy zone.

If any math professionals are reading this, please translate what you know about factorials and their uses into halfwit language, and explain to us confident idiots how the pros actually do this. I'll give you a Starbucks gift coupon, which you can transform into theorems.

  • While this answer provides additional info regarding factorial, you should still edit it so that it directly address the issue with the code in the question. – Weijun Zhou Jul 15 '23 at 10:12