float to int转换错误(即使float已经是int)

时间:2017-08-23 08:16:21

标签: c++ c++11

我正在编写一个函数来使用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。

4 个答案:

答案 0 :(得分:3)

浮点数具有与之关联的舍入错误。这是一篇关于这个主题的好文章:What Every Computer Scientist Should Know About Floating-Point Arithmetic

在您的情况下,浮点数值保持非常接近但小于21的值。 implicit floating–integral conversions的规则说:

  

小数部分被截断,即小数部分是截断的   丢弃。

std::nearbyint

  

使用当前的舍入模式将浮点参数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)

来自this std::tgamma reference

  

如果arg是自然数,new { Name = "groupRadio" }是arg-1的阶乘。如果参数是足够小的整数,许多实现计算精确的整数域因子。

您正在使用的编译器正在执行此操作,计算表达式std::tgamma(arg)的{​​{1}}的阶乘。

结果可能在编译器之间以及优化级别之间有所不同。如Jonas所示,optimizedunoptimized版本之间存在很大差异。

答案 3 :(得分:0)

问题在于处理floats。 将2 2作为1.99999而不是ceil。 因此,转换为int将删除小数部分。

因此,通过调用cmathmath.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}}