[英]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.