[英]Floating point error in C++ code
我正在尝试解决一个问题,在这个问题中,我需要找出组建两个成员的团队的可能方法。(注意:一个团队最多可以有两个人)编写此代码后,它可以正常工作,但在某些情况下测试用例显示浮点错误,我无法确切知道它是什么。
输入:第一行:测试用例数第二行:总人数
谢谢
#include<iostream>
using namespace std;
long C(long n, long r)
{
long f[n + 1];
f[0] = 1;
for (long i = 1; i <= n; i++)
{
f[i] = i * f[i - 1];
}
return f[n] / f[r] / f[n - r];
}
int main()
{
long n, r, m,t;
cin>>t;
while(t--)
{
cin>>n;
r=1;
cout<<C(n, min(r, n - r))+1<<endl;
}
return 0;
}
您没有使用浮点数。 而且您似乎正在使用可变大小的数组,这是C的功能,可能是C ++的扩展,但不是标准的。
无论如何,即使n的值很小,也将发生溢出,从而导致不确定的行为。
实际上,溢出将导致数组元素在n值不大的情况下变为零。
然后,您的代码将被零除并崩溃。
他们也可能有一个测试用例,例如(1000000000,999999999),这很容易解决,但对于我敢打赌的您的代码而言,它不会崩溃。
您没有浮点异常。 您将得到除以零的例外。 因为您的代码正在尝试除以数字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]
所有后续分配也将变为零。 在C
内部的主循环结束之后, f[nr]
为零。 因此,除以零误差。
更新
我回过头来,对您的问题进行了反向工程。 看来您的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;
}
数组语法为:
type name[size]
注意:大小必须为常数而不是变量
范例1:
int name[10];
范例2:
const int asize = 10;
int name[asize];
您没有指定“浮点错误”的含义-我认为您是指您正在执行整数除法而不是浮点除法这样的事实,以便始终获得整数而不是浮点数。
int a, b;
a = 7;
b = 2;
std::cout << a / b << std::endl;
这将导致3,而不是3.5! 如果要浮点结果,则应使用浮点数,如下所示:
float a, b;
a = 7;
b = 2;
std::cout << a / b << std::end;
因此,解决问题的方法只是使用float
而不是long long int
。
另请注意,您正在使用在C ++中不起作用的可变大小数组-为什么不使用std::vector
?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.