2016-03-02 3 views
3

Я экспериментирую с C++. Я написал простую функцию, которая находит область треугольника, используя очень большие числа.C++ Точность с использованием длинного длинного с двойным

Я передаю две большие цифры в функцию, и из отдельной функции getValue() я возвращаю отдельное значение из другого уравнения. Мне интересно, почему, когда я помещаю 1 на линии вне круглых скобках, например:

return (long long)(a - b/2.0) + b + 1; 

я получить значение 9007200509008001

И когда я держу 1 внутри круглых скобок, например, так:

return (long long)(a - b/2.0 + 1) + b; 

Я получаю 9007200509008000

Второе значение на единицу меньше, чем первый, хотя вычисление должно привести в равных ответов.

#include <iostream> 

double triangleArea(int b, int h) 
{ 
    long long bh = (long long)b * h; 
    return 0.5 * bh; 
} 

long long getValue() 
{ 
    int deltaY = 18014400; 
    int deltaX = 1000000000; 

    long b = deltaY + deltaX + 1600; 
    double a = triangleArea(deltaX, deltaY); 
    return (long long)(a - b/2.0) + b + 1; 
} 

int main(int argc, char **argv) 
{ 
    std::cout << getValue() << std::endl; 
} 

Я уверен, что ответ, вероятно, очевиден для некоторых людей, но я не могу обернуть вокруг него голову. Может кто-нибудь объяснить?

+0

все о двойной точности и представления. при работе с очень большими удвоениями вы можете иметь некоторые значения, которые не могут измениться при добавлении к ним небольшого числа, как в этом случае. когда 1 наружная скобка у вас долгое время не страдает от таких проблем – vlad1slav

+0

Поскольку вы всегда пытаетесь получить целые значения, это не означает использование значений «double», так как оно потеряло точность.Вы можете написать это: 'return static_cast (a) - (b/2) + 1 + b;' – Mine

ответ

6

Когда вы делите на 2.0, у вас есть неявное преобразование в double. Двойная точность ограничена примерно 15 десятичными знаками. Итак, что происходит с цифрой 16 th, отлично определено, но может и не быть тем, что вы ожидаете.

Если вам нужна точность, попробуйте использовать long long или unsigned long long и тщательно контролируйте, чтобы у вас не было переполнения. Если этого недостаточно, вам придется использовать арифметику с несколькими точками (например, GMP). Поскольку, как только вы используете двойные плавающие точки, вы ограничены 53 двоичными цифрами точности или примерно 15-16 десятичными цифрами.

-1

long long обычно имеет более высокую точность, чем double. Например, если оба являются 64 битами, то long long имеет точность 64 бит, но double имеет только 53-битную точность (52-битная мантисса с подразумеваемым msb = 1).

В вашем случае, это означает, что a - b/2.0 + 1 такого же double константы, как a - b/2.0, но (long long)(a - b/2.0) + b + 1 не то же самое long long константы, как (long long)(a - b/2.0) + b.

0

Проблема заключается в неявном броске, чтобы удвоить ваше разделение и переменную a.

Заканчивать следующий образец для оформления:

#include <iostream> 

double triangleArea(int b, int h) 
{ 
    long long bh = (long long)b * h; 
    return 0.5 * bh; 
} 

long long getValue() 
{ 
    int deltaY = 18014400; 
    int deltaX = 1000000000; 

    long b = deltaY + deltaX + 1600; 
    double a = triangleArea(deltaX, deltaY); 

    long long c = b/2.0; 

    long long d1 = a - c; 
    long long d2 = ((long long)a) - c + 1; 

    long long e1 = d1 + b + 1; 
    long long e2 = d2 + b; 

    return e1; 
} 

int main(int argc, char **argv) 
{ 
    std::cout << getValue() << std::endl; 
} 

Явное приведение к long long решает вашу особую проблему там.

Будьте в курсе вашей переменной точности и преобразования между ними.