为什么(int)55 == 54在C ++中?

时间:2009-02-16 17:54:16

标签: c++ fibonacci

所以我正在学习C ++。我已经获得了“C ++编程语言”和“有效的C ++”,而且我正在运行Project Euler。问题1 ... dunzo。问题2 ......没那么多。我在Win32控制台应用程序中使用VS2008。

斐波纳契数列的所有偶数项的总和是400万?

它没有工作,所以我减少了100个测试用例...

这是我写的......

// Problem2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    cout << "Project Euler Problem 2:\n\n";
    cout << "Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:\n\n";
    cout << "1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...\n\n";
    cout << "Find the sum of all the even-valued terms in the sequence which do not exceed four million.\n\n";
    cout << "Answer:  " << Solve();
}

double Solve() {
    int FibIndex = 0;
    double result = 0.0;
    double currentFib = GenerateNthFibonacciNumber(FibIndex);
    while (currentFib < 100.0){
        cout << currentFib << " " << (int)currentFib << " " << (int)currentFib % 2 << "\n";
        if ((int)currentFib % 2 == 0){
            result += currentFib;
            cout<<(int)currentFib;
        }
        currentFib = GenerateNthFibonacciNumber(++FibIndex);
    }
    return result;
}

double GenerateNthFibonacciNumber(const int n){
    //This generates the nth Fibonacci Number using Binet's Formula
    const double PHI = (1.0 + sqrt(5.0)) / 2.0;
    return ((pow(PHI,n)-pow(-1.0/PHI,n)) / sqrt(5.0));
}

这是输出......

  

项目欧拉问题2:

     

Fibonacci中的每个新术语   序列是通过添加   前两个任期。从1开始   2,前10个术语将是:

     

1,2,3,5,8,13,21,34,55,89,......

     

找出所有偶数值的总和   序列中没有的术语   超过四百万。

     

0 0 0
1 1 1 1 1 1 2 2   0
3 3 1
5 5 1
8 8 0

  13 1
21 21 1
34 34 0
55 54   0
89 89 1
答案:99

所以我有三列调试代码...从generate函数返回的数字,(int)generatedNumber和(int)generatedNumber%2

所以在第11个学期我们有

55,54,0

为什么(int)55 = 54?

由于

8 个答案:

答案 0 :(得分:56)

转换为int会截断数字 - 就像您调用floor(currentFib)一样。因此,即使currentFib54.999999 ...(数字如此接近55,打印时会被舍入),(int)currentFib也会产生54。

答案 1 :(得分:13)

由于浮点舍入,55行计算的是54.99999。 将double转换为int会截断.99999。

在我的机器上,打印显示(currentFib-(int)currentFib)的列会显示1.42109e-14的错误。所以它更像是0.999999999999986。

答案 2 :(得分:4)

好的,简短的回答是,在任何情况下都不应该(int)55 == 54,所以你需要开始问自己相关的代码行是什么。

第一个问题:与类型转换相比,==绑定的强度有多大?

答案 3 :(得分:4)

Shog9是正确的,使用double类型来解决这个问题,如果你要将内容强制转换为最好的方法。如果您的编译器支持它,您应该使用long long或其他64位整数类型,这几乎肯定会保留所有偶数项的总和小于4百万Fibonacci序列的结果。

如果我们使用Fibonacci序列遵循奇数奇数甚至奇数奇数偶数的事实......如下所示应该做的伎俩。

...
unsigned int fib[3];
fib[0]=1;
fib[1]=1;
fib[2]=2;

unsigned long long sum=0;

while(fib[2]<4000000)
{
    sum+=fib[2];

    fib[0]=(fib[1]+fib[2]);
    fib[1]=(fib[2]+fib[0]);
    fib[2]=(fib[0]+fib[1]);
}

std::cout<<"The sum is: "<<sum<<". \n";
....
应该做的伎俩,可能有更快的方法,但这个方法非常直接,易于阅读。

看着它我意识到你可能会使用一个标准的无符号32位整数作为和数,但我会保留它以防万一。

此外,您的代码会对生成第n个Fibonacci数函数进行大量函数调用。一个不错的优化编译器会内联这些调用,但如果没有,那么事情会变慢,因为函数调用比其他技术更昂贵。

答案 4 :(得分:2)

我同意100%使用shog9的答案 - 使用你用来计算Fibonacci的算法,你必须非常小心浮点值。我发现页面cubbi.com: fibonacci numbers in c++似乎显示了获取它们的其他方式。

我四处寻找一个关于如何使GenerateNthFibonacciNumber处理它返回双54.999999的情况的好主意,但当你转换为int或long时,你得到54。

我在C++ Rounding看到了一个合理的解决方案,我在下面的代码中进行了调整。

此外,这不是一个巨大的交易,但您可能想要预先计算PHI,然后将其作为参数传递或将其作为全局引用 - 现在您每次调用时都会重新计算它功能

double GenerateNthFibonacciNumber(const int n)
{
        //This generates the nth Fibonacci Number using Binet's Formula   
        const double PHI = (1.0 + sqrt(5.0)) / 2.0;
        double x = ((pow(PHI,n)-pow(-1.0/PHI,n)) / sqrt(5.0));
        // inspired by http://www.codingforums.com/archive/index.php/t-10827.html
        return ((x - floor(x)) >= 0.5) ? ceil(x) : floor(x);
}

最后,这是我如何重写你的Solve()方法,以便只在代码中的一个地方调用GenerateNthFibonacciNumber(FibIndex)。我还在列中添加了当前运行总计甚至Fibonacci项的列到您的输出:

double Solve() {
    long FibIndex = 0;
    double result = 0.0;
    double oldresult = 0.0;
    int done = 0;
    const double PHI = (1.0 + sqrt(5.0)) / 2.0;

    while (!done)
    {
        double currentFib = GenerateNthFibonacciNumber(++FibIndex);
        if ((int)currentFib % 2 == 0)
        {
            oldresult = result;

            if (currentFib >= 4000000.0)
            {
                done = 1;
            }
            else
            {
                result += currentFib;
            }

        }
        cout << currentFib << " " << (int)currentFib << " " << (int)currentFib % 2 << " " << (int)result << "\n";       
    }
    return result;
}

答案 5 :(得分:2)

我知道这对你的真实问题没有帮助,但是你提到你正在学习C ++。出于学习目的,我建议尽可能接近ANSI。我认为MSVC上的/Za(你可能正在使用它),或GCC上的-ansi -pedantic

特别是,您应该使用其中一个签名main,直到您有一个良好的(特定于平台的)原因:

int main(int argc, char *argv[]);
int main(int argc, char **argv); // same as the first
int main();

...而不是任何特定于平台的版本,例如此(仅限Windows)示例:

#include <windows.h> // defines _TCHAR and _tmain
int _tmain(int argc, _TCHAR* argv[]); // win32 Unicode vs. Multi-Byte

答案 6 :(得分:2)

以上所有不使用整数数学浮点值的建议都值得注意!

如果要对正浮点值进行整数“舍入”,以使小数分量小于0.5的值舍入到下一个最小整数,小数分量为0.5或更大的值舍入到下一个更高的整数,例如< / p>

0.0 = 0
0.1 = 0
0.5 = 1
0.9 = 1
1.0 = 1
1.1 = 1
...etc...    

将0.5加到你正在施放的值上。

double f0 = 0.0;
double f1 = 0.1;
double f2 = 0.5;
double f3 = 0.9;

int i0 = ( int )( f0 + 0.5 );  // i0 = 0
int i1 = ( int )( f1 + 0.5 );  // i1 = 0
int i2 = ( int )( f2 + 0.5 );  // i2 = 1
int i3 = ( int )( f3 + 0.5 );  // i3 = 1

答案 7 :(得分:0)

打破代码生成,有时浮点数不会像我们在长表达式中使用时那样思考...打破代码并检查它。