我正在编写一个函数来使用c ++提供的tgamma函数来计算二项式系数。 tgamma返回浮点值,但我想返回一个整数。请看一下这个示例程序,比较将float转换回int的三种方法:
#include <iostream>
#include <cmath>
int BinCoeffnear(int n,int k){
return std::nearbyint( std::tgamma(n+1) / (std::tgamma(k+1)*std::tgamma(n-k+1)) );
}
int BinCoeffcast(int n,int k){
return static_cast<int>( std::tgamma(n+1) / (std::tgamma(k+1)*std::tgamma(n-k+1)) );
}
int BinCoeff(int n,int k){
return (int) std::tgamma(n+1) / (std::tgamma(k+1)*std::tgamma(n-k+1));
}
int main()
{
int n = 7;
int k = 2;
std::cout << "Correct: " << std::tgamma(7+1) / (std::tgamma(2+1)*std::tgamma(7-2+1)); //returns 21
std::cout << " BinCoeff: " << BinCoeff(n,k); //returns 20
std::cout << " StaticCast: " << BinCoeffcast(n,k); //returns 20
std::cout << " nearby int: " << BinCoeffnear(n,k); //returns 21
return 0;
}
为什么,即使计算返回一个等于21的浮点数,&#39;正常&#39;转换失败,只有nearbyint返回正确的值。实现这个的最好方法是什么?
编辑:根据c ++文档here tgamma(int)返回一个double。
答案 0 :(得分:3)
浮点数具有与之关联的舍入错误。这是一篇关于这个主题的好文章:What Every Computer Scientist Should Know About Floating-Point Arithmetic。
在您的情况下,浮点数值保持非常接近但小于21
的值。 implicit floating–integral conversions的规则说:
小数部分被截断,即小数部分是截断的 丢弃。
使用当前的舍入模式将浮点参数arg以浮点格式舍入为整数值。
在这种情况下,浮点数将完全为21
,以下隐式转换将返回21
。
第一个cout
输出21
,因为默认情况下在cout
中进行舍入。请参阅std::setprecition
。
这是live example。
实现此目的最好的方法是什么?
使用完整的整数阶乘函数,它返回unsigned int
而不是tgamma
。
答案 1 :(得分:3)
@nos的评论很重要。注意第一行
std::cout << "Correct: " <<
std::tgamma(7+1) / (std::tgamma(2+1)*std::tgamma(7-2+1));
打印double
值,不执行浮点到整数转换。
以浮点计算的结果确实小于21,但此double precision
值由cout
打印为21。
在我的机器上(x86_64,gnu libc,g ++ 4.8,优化级别0)设置cout.precision(18)
使结果显式。
Correct: 20.9999999999999964 BinCoeff: 20 StaticCast: 20 nearby int: 21
在这种情况下实际上用浮点运算替换整数运算,但必须记住结果必须是整数。目的是使用std::round
。
std::nearbyint
的问题在于,根据舍入模式,它可能会产生不同的结果。
std::fesetround(FE_DOWNWARD);
std::cout << " nearby int: " << BinCoeffnear(n,k);
将返回20。
因此,对于std::round
,BinCoeff函数可能看起来像
int BinCoeffRound(int n,int k){
return static_cast<int>(
std::round(
std::tgamma(n+1) /
(std::tgamma(k+1)*std::tgamma(n-k+1))
));
}
答案 2 :(得分:3)
如果arg是自然数,
new { Name = "groupRadio" }
是arg-1的阶乘。如果参数是足够小的整数,许多实现计算精确的整数域因子。
您正在使用的编译器正在执行此操作,计算表达式std::tgamma(arg)
的{{1}}的阶乘。
结果可能在编译器之间以及优化级别之间有所不同。如Jonas所示,optimized和unoptimized版本之间存在很大差异。
答案 3 :(得分:0)
问题在于处理floats
。
将2
2
作为1.99999
而不是ceil
。
因此,转换为int将删除小数部分。
因此,通过调用cmath
或math.h
中声明的#include <iostream>
#include <cmath>
int BinCoeffnear(int n,int k){
return std::nearbyint( std::tgamma(n+1) / (std::tgamma(k+1)*std::tgamma(n-k+1)) );
}
int BinCoeffcast(int n,int k){
return static_cast<int>( ceil(std::tgamma(n+1) / (std::tgamma(k+1)*std::tgamma(n-k+1))) );
}
int BinCoeff(int n,int k){
return (int) ceil(std::tgamma(n+1) / (std::tgamma(k+1)*std::tgamma(n-k+1)));
}
int main()
{
int n = 7;
int k = 2;
std::cout << "Correct: " << (std::tgamma(7+1) / (std::tgamma(2+1)*std::tgamma(7-2+1))); //returns 21
std::cout << " BinCoeff: " << BinCoeff(n,k); //returns 20
std::cout << " StaticCast: " << BinCoeffcast(n,k); //returns 20
std::cout << " nearby int: " << BinCoeffnear(n,k); //returns 21
std::cout << "\n" << (int)(2.9995) << "\n";
}
函数w / c,而不是立即转换为int。
此代码将返回全部21
{{1}}