Подтвердить что ты не робот

Почему (int) 55 == 54 в С++?

Итак, я изучаю С++. У меня есть "Язык программирования С++" и "Эффективный С++", и я запускаю Project Euler. Проблема 1... dunzo. Проблема 2... не так много. Я работаю в VS2008 в консольном приложении Win32.

Какова сумма всех четных членов последовательности Фибоначчи под 4 миллионами?

Он не работал, поэтому я сократил тестовый пример из 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 Formula
    const double PHI = (1.0 + sqrt(5.0)) / 2.0;
    return ((pow(PHI,n)-pow(-1.0/PHI,n)) / sqrt(5.0));
}

И вот вывод...

Проект Эйлера Задача 2:

Каждый новый член в Фибоначчи последовательность создается путем добавления предыдущие два условия. Начиная с 1 и 2, первые 10 членов будут:

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

Найдите сумму всех четных термины в последовательности, которые не превышают четыре миллиона.

0 0 0 1 1 1 1 1 1 1 2 2 0
3 3 1
5 5 1
8 8 0
13 13 1
21 21 1
34 34 0
55 54 0
89 89 1
Ответ: 99

Итак, у меня есть три столбца кода отладки... номер, возвращаемый функцией generate, (int) сгенерированный номер и (int) createdNumber% 2

Итак, на 11-м члене имеем

55,54,0

Почему (int) 55 = 54?

Спасибо

4b9b3361

Ответ 1

Отбрасывание до int сокращает число - так же, как если бы вы звонили floor(currentFib). Так что даже если currentFib 54.999999... (число, столь близкое к 55, что оно будет округлено при печати), (int)currentFib произведет 54.

Ответ 2

Из-за округления с плавающей запятой эта строка 55 вычисляет что-то вроде 54.99999. Кастинг с двойным на int урезает .99999.

На моей машине печать столбца с отображением (currentFib-(int)currentFib) показывает ошибки порядка 1.42109e-14. Так что это больше похоже на 0.999999999999986.

Ответ 3

Хорошо, короткий ответ заключается в том, что без условия should (int) 55 == 54, поэтому вам нужно начать спрашивать себя, что делает соответствующая строка кода.

Первый вопрос: насколько сильно == связывается с типом?

Ответ 4

Shog9 имеет это право, используя тип double для такой проблемы, это не лучший способ пойти, если вы собираетесь бросать вещи в ints. Если ваш компилятор поддерживает его, вы должны использовать длинный длинный или какой-нибудь другой 64-битный целочисленный тип, который почти наверняка будет содержать результат суммы всех четных членов менее 4 миллионов от последовательности Фибоначчи.

Если мы используем тот факт, что последовательность Фибоначчи следует шаблону нечетным четным четным четным нечетным даже... что-то вроде этого должно сделать трюк.

...
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-й функции числа Фибоначчи. Хороший оптимизирующий компилятор будет выстраивать эти вызовы, но если это не произойдет, все будет замедляться, так как вызовы функций более дороги, чем другие методы.

Ответ 5

Я согласен на 100% с ответом shog9 - с помощью алгоритма, который вы использовали для вычисления Фибоначчи, вы должны быть очень осторожны с значениями с плавающей запятой. Я обнаружил, что страница cubbi.com: число фибоначчи в С++, похоже, показывает другие способы их получения.

Я оглянулся вокруг, чтобы получить хорошую идею о том, как сделать вашу реализацию GenerateNthFibonacciNumber обрабатывать случаи, когда она возвращает двойной 54.999999, но когда вы добавили int или long, вы получите 54.

Я столкнулся с тем, что кажется разумным решением на С++ Rounding, который я адаптировал ниже в вашем код.

Кроме того, это не сделка огромная, но вы можете предварительно вычислить PHI, либо передать ее как параметр, либо ссылаться на нее как глобальную - теперь вы пересчитываете ее каждый раз, когда вы вызываете функция.

double GenerateNthFibonacciNumber(const int n)
{
        //This generates the nth Fibonacci Number using Binet 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) вызывается только в одном месте в коде. Я также добавил столбец с текущим текущим итогом четных условий Фибоначчи к вашему выходу:

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;
}

Ответ 6

Я знаю, что это не поможет с вашим реальным вопросом, но вы упомянули, что изучаете С++. Я бы порекомендовал держать как можно ближе к ANSI для учебных целей. Я думаю, что /Za на MSVC (что вы, вероятно, используете), или -ansi -pedantic на GCC.

В частности, вы должны использовать одну из этих подписей для 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

Ответ 7

Все вышеприведенные предложения не использовать значения с плавающей запятой для целочисленной математики заслуживают внимания!

Если вам нужно целое "округление" для положительных значений с плавающей запятой, чтобы значения с дробным компонентом ниже 0,5 раунда до следующего младшего целого числа, а значения с дробным компонентом 0,5 или более округлялись до следующего более высокого целого числа, например

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

Ответ 8

Разбить код для генератора, иногда число с плавающей запятой не ведет себя так, как мы думаем, когда используется в длинном выражении... сломайте код и проверьте его.