您没有得到浮点异常。您将获得除以零的异常。因为您的代码试图除以数字 0(这不能在计算机上完成)。
当您调用 C(100, 1) 时,初始化 C 内的 f 数组的主循环呈指数增长。最终,两个值相乘,使得i * f[i-1] 由于溢出而为零。这导致所有后续的 f[i] 值都被初始化为零。然后循环后面的除法就是被零除。
尽管这些论坛上的纯粹主义者会说这是未定义的,但这就是大多数 2 的补码架构上真正发生的情况。或者至少在我的电脑上....
i==21:
f[20] 已经等于2432902008176640000
21 * 2432902008176640000 对于 64 位有符号溢出,通常会变为 -4249290049419214848 所以此时,您的程序存在错误,现在处于未定义行为。
i==66
f[65] 等于 0x8000000000000000。所以 66 * f[65] 被计算为零,原因对我来说是有意义的,但应该被理解为未定义的行为。
f[66] 分配为 0,f[i] 的所有后续分配也将变为 0。 C 内部的主循环结束后,f[n-r] 为零。因此,除以零误差。
更新
我回去对您的问题进行了逆向工程。您的 C 函数似乎只是在尝试计算这个表达式:
N!
-------------
R! * (N-R)!
这是“唯一排序组合的数量”
在这种情况下,我们可以将表达式简化为:
n
[ ∏ i ]
n-r
--------------------
R!
这不会消除溢出,但会让您的 C 函数能够采用更大的 N 和 R 值来计算组合数而不会出错。
但我们也可以在尝试做一个大的长阶乘表达式之前利用简单的归约
例如,假设我们试图计算 C(15,5)。数学上是:
15!
--------
10! 5!
或者如我们上面所说的:
1*2*3*4*5*6*7*8*9*10*11*12*13*14*15
-----------------------------------
1*2*3*4*5*6*7*8*9*10 * 1*2*3*4*5
分子和分母的前10个因数相互抵消:
11*12*13*14*15
-----------------------------------
1*2*3*4*5
但直观地,您可以看到分子中的“12”已经可以被分母 2 和 3 整除。而分子中的 15 可以被分母中的 5 整除。所以可以应用简单的归约:
11*2*13*14*3
-----------------------------------
1 * 4
最大公约数减少的空间更大,但这是一个很好的开始。
让我们从一个计算列表中所有值的乘积的辅助函数开始。
long long multiply_vector(std::vector<int>& values)
{
long long result = 1;
for (long i : values)
{
result = result * i;
if (result < 0)
{
std::cout << "ERROR - multiply_range hit overflow" << std::endl;
return 0;
}
}
return result;
}
在做归约操作后,不要像使用上述函数那样实现C
long long C(int n, int r)
{
if ((r >= n) || (n < 0) || (r < 0))
{
std::cout << "invalid parameters passed to C" << std::endl;
return 0;
}
// compute
// n!
// -------------
// r! * (n-r)!
//
// assume (r < n)
// Which maps to
// n
// [∏ i]
// n - r
// --------------------
// R!
int end = n;
int start = n - r + 1;
std::vector<int> numerators;
std::vector<int> denominators;
long long numerator = 1;
long long denominator = 1;
for (int i = start; i <= end; i++)
{
numerators.push_back(i);
}
for (int i = 2; i <= r; i++)
{
denominators.push_back(i);
}
size_t n_length = numerators.size();
size_t d_length = denominators.size();
for (size_t n = 0; n < n_length; n++)
{
int nval = numerators[n];
for (size_t d = 0; d < d_length; d++)
{
int dval = denominators[d];
if ((nval % dval) == 0)
{
denominators[d] = 1;
numerators[n] = nval / dval;
}
}
}
numerator = multiply_vector(numerators);
denominator = multiply_vector(denominators);
if ((numerator == 0) || (denominator == 0))
{
std::cout << "Giving up. Can't resolve overflow" << std::endl;
return 0;
}
long long result = numerator / denominator;
return result;
}