抱歉,我觉得这很愚蠢,我准备失去一半的问题,但为什么这个算法不起作用?它起到了一定的作用。数字13后,阶乘有点偏。例如,数字在数十万个地方和之后并不完全匹配。
#include <stdio.h>
float factorial(unsigned int i) {
if (i <= 1) {
return 1;
}
return i * factorial(i - 1);
}
int main() {
int i = 13;
printf("Factorial of %d is %f\n", i, factorial(i));
return 0;
}
这是输出:
Factorial of 13 is 6227020800.000000
以下是不准确输出的示例:
Factorial of 14 is 87178289152.000000
数字14的输出实际上应该是这个(来自mathisfun.com)
14 87,178,291,200
我将返回类型更改为float以获得更准确的输出,但我从这里获得了大部分代码:https://www.tutorialspoint.com/cprogramming/c_recursion.htm
编辑:如果我更改为返回类型以使输出翻倍,则准确到21。我正在使用%Lf 字符串格式化程序来输出< em> printf 功能。
答案 0 :(得分:11)
简单。 float
无法在不损失精度的情况下准确存储16777216以上的整数。
int
比浮动更好。但请尝试long long
,以便正确存储19位数字。
答案 1 :(得分:3)
OP遇到float
的精度限制。对于typical float
,16777216.0f
以上的整数值并非所有都可以完全表示。 此点之上的某些阶乘结果可以准确表示。
让我们尝试不同的类型
在11!
,float
结果超出16777216.0f
且完全正确
在14!
,由于精度有限,float
结果不准确。
在23!
,由于精度有限,double
结果不准确。
在21!
,答案超出我的uintmax_t
范围
在35!
,答案超出了float
范围
在171!
,答案超出了double
范围。
string 表示无穷无尽,直到达到缓冲区限制。
#include <stdint.h>
#include <string.h>
#include <stdio.h>
uintmax_t factorial_uintmax(unsigned int i) {
if (i <= 1) {
return 1;
}
return i * factorial_uintmax(i - 1);
}
float factorial_float(unsigned int i) {
if (i <= 1) {
return 1;
}
return i * factorial_float(i - 1);
}
double factorial_double(unsigned int i) {
if (i <= 1) {
return 1;
}
return i * factorial_double(i - 1);
}
char * string_mult(char *y, unsigned base, unsigned x) {
size_t len = strlen(y);
unsigned acc = 0;
size_t i = len;
while (i > 0) {
i--;
acc += (y[i] - '0') * x;
y[i] = acc % base + '0';
acc /= base;
}
while (acc) {
memmove(&y[1], &y[0], ++len);
y[0] = acc % base + '0';
acc /= base;
}
return y;
}
char *factorial_string(char *dest, unsigned int i) {
strcpy(dest, "1");
for (unsigned m = 2; m <= i; m++) {
string_mult(dest, 10, m);
}
return dest;
}
void factorial_test(unsigned int i) {
uintmax_t u = factorial_uintmax(i);
float f = factorial_float(i);
double d = factorial_double(i);
char s[2000];
factorial_string(s, i);
printf("factorial of %3d is uintmax_t: %ju\n", i, u);
printf(" float: %.0f %s\n", f, "*" + (1.0 * f == u));
printf(" double: %.0f %s\n", d, "*" + (d == u));
printf(" string: %s\n", s);
}
int main(void) {
for (unsigned i = 11; i < 172; i++)
factorial_test(i);
return 0;
}
输出
factorial of 11 is uintmax_t: 39916800
float: 39916800
double: 39916800
string: 39916800
factorial of 12 is uintmax_t: 479001600
float: 479001600
double: 479001600
string: 479001600
factorial of 13 is uintmax_t: 6227020800
float: 6227020800
double: 6227020800
string: 6227020800
factorial of 14 is uintmax_t: 87178291200
float: 87178289152 *
double: 87178291200
string: 87178291200
factorial of 20 is uintmax_t: 2432902008176640000
float: 2432902023163674624 *
double: 2432902008176640000
string: 2432902008176640000
factorial of 21 is uintmax_t: 14197454024290336768
float: 51090940837169725440 *
double: 51090942171709440000 *
string: 51090942171709440000
factorial of 22 is uintmax_t: 17196083355034583040
float: 1124000724806013026304 *
double: 1124000727777607680000 *
string: 1124000727777607680000
factorial of 23 is uintmax_t: 8128291617894825984
float: 25852017444594485559296 *
double: 25852016738884978212864 *
string: 25852016738884976640000
factorial of 34 is uintmax_t: 4926277576697053184
float: 295232822996533287161359432338880069632 *
double: 295232799039604119555149671006000381952 *
string: 295232799039604140847618609643520000000
factorial of 35 is uintmax_t: 6399018521010896896
float: inf *
double: 10333147966386144222209170348167175077888 *
string: 10333147966386144929666651337523200000000
factorial of 170 is uintmax_t: 0
float: inf *
double: 72574156153079940453996357155895914678961840000000... *
string: 72574156153079989673967282111292631147169916812964...
factorial of 171 is uintmax_t: 0
float: inf *
double: inf *
string: 12410180702176678234248405241031039926166055775016...
答案 2 :(得分:2)
为什么这种因子算法不准确
你的algorithm
就没有错。只是您使用的数据类型对它们可以存储的最大数量有限制。无论您选择哪种算法,这都是一个问题。您可以将数据类型从float
更改为类似long double
的内容,以保存更大的内容。但是一旦阶乘值超过该数据类型的容量,它最终仍会失败。在我看来,如果传入的参数大于你选择的数据类型可以支持的值,你应该在你的阶乘函数中放一个条件而不计算任何东西。
答案 3 :(得分:2)
float
可以表示比int
更宽的范围数字,但它不能代表该范围内的所有值 - 当您接近范围的边缘时(即,随着值的大小增加),可表示值之间的差距变得更大。
例如,如果您不能表示0.123和0.124之间的值,那么您也不能表示123.0和124.0之间,或1230.0和1240.0,或12300.0和12400.0等之间的值(当然,IEEE-754单精度{ {1}}比 更精确。
话虽如此,float
应该能够准确地表示最多2 24 的所有整数值,所以我打赌这个问题在{{1}调用 - float
参数是&#34;促进&#34;至printf
,因此涉及到代理更改,这可能会导致精确度下降。
尝试将float
的返回类型更改为double
,看看是否有帮助。
&lt; gratuitous rant&gt;
每次我看到一个递归因子函数我都想尖叫。在这种特殊情况下,递归在代码清晰度或性能方面都没有改进迭代解决方案:
factorial
并且由于这么多函数调用的开销,实际上会导致更差的性能。
是的,阶乘的定义是递归的,但是阶乘函数的实现并不是必须的。 Fibonacci序列相同。甚至还有斐波那契数字的封闭形式解决方案
double
首先不需要任何循环。
递归非常适合将数据划分为相对较少的,相等大小的子集(Quicksort,树遍历等)的算法。对于这样的事情,分区是1个元素的N-1个子集?没那么多。
&lt; / gratuitous rant&gt;
答案 4 :(得分:2)
有人在前一段时间发布了类似的问题。大家的共识是,如果你正在为工作而使用大量的库(如GMP),并且如果它是编程练习,则使用字符数组编写解决方案。
例如:
/* fact50.c
calculate a table of factorials from 0! to 50! by keeping a running sum of character digits
*/
#include <stdio.h>
#include <string.h>
int main (void)
{
printf ("\n Table of Factorials\n\n");
// length of arrays = 65 character digits
char str[] =
"00000000000000000000000000000000000000000000000000000000000000000";
char sum[] =
"00000000000000000000000000000000000000000000000000000000000000001";
const int len = strlen (str);
int index;
for ( int i = 0; i <= 50; ++i ) {
memcpy (str, sum, len);
for ( int j = 1; j <= i - 1; ++j ) {
index = len - 1;
int carry = 0;
do {
int digit = (sum[index] - '0') + (str[index] - '0') + carry;
carry = 0;
if ( digit > 9 ) {
carry = 1;
digit %= 10;
}
sum[index] = digit + '0';
--index;
}
while ( index >= 0 );
}
printf ("%2i! = ", i);
for ( index = 0; sum[index] == '0'; ++index )
printf ("%c", '.');
for ( ; index < len; ++index )
printf ("%c", sum[index]);
printf ("\n");
}
return 0;
}