Как правильно разделить две пары - PullRequest
0 голосов
/ 08 октября 2009

у меня два

double a, b;

Я знаю, что верно следующее

-1 <= a/b <= 1

однако b может быть сколь угодно малым. Когда я делаю это наивно и просто вычисляю значение

 a/b

условие, указанное выше, не выполняется в некоторых случаях, и я получаю значения, которые намного больше 1 в абсолютном значении (например, 13 или 14.)

Как я могу гарантировать, что когда я делю на b, я получаю значение, такое, что вышеупомянутое условие может быть выполнено. В случае, когда я не могу этого гарантировать, я с радостью установлю вычисленное значение a / b равным 0.

Ответы [ 7 ]

10 голосов
/ 08 октября 2009

Деление в системе IEEE-754 является правильно округленной операцией, что означает, что если переполнение или переполнение не происходит, результат всегда будет в пределах 0,5 "ulp" математического "бесконечно точного" результата , Если говорить не на языке FP, это означает, что результат всегда будет в пределах примерно 2 ^ -53 от точного ответа. Поскольку вы знаете, что бесконечно точный результат находится в диапазоне от -1 до 1, переполнение не может произойти; недостаточное количество может, но это приведет к очень, очень близким к нулю числам, а не к порядку 13.

Либо ваше условие фактически не выполняется, либо вы находитесь в системе, в которой нет арифметики IEEE-754, или в вашем коде есть ошибка. Можете ли вы опубликовать значения a и b, которые генерируют этот результат, и код, который вы используете для деления и печати результата?

10 голосов
/ 08 октября 2009

То, что вам нужно обеспечить, это abs (a) ≤abs (b). Если это условие выполняется, то -1≤a / b≤1, независимо от используемой точности с плавающей точкой. Ваша логическая ошибка происходит перед делением, поскольку в точке деления abs (a)> abs (b) нарушается априорное требование.

1 голос
/ 08 октября 2009

Очень маловероятно, что вы на самом деле вызываете потерю данных из-за недостаточного количества данных.Хотя возможно, что у двойников невероятный диапазон, и вы вряд ли его достигнете.

Я думаю, проблема кроется где-то до этого.У вас либо логическая ошибка, либо вы просто поглощаете доступную точность с помощью множества операций.Будьте особенно осторожны с дополнениями и вычитаниями.1E20 + 1 = 1E20.

Если это связано с потерей точности, вам придется переделать свою программу или прибегнуть к библиотеке произвольной точности для своей математики.(Осторожно - SLOW )

0 голосов
/ 08 октября 2009

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

float Calculate(float a, float b)
{
    if (abs(a) > abs(b))
    {
        return 0;
    }
    else
    {
        return a/b;
    }
}
0 голосов
/ 08 октября 2009

Единственные ситуации, которые я могу придумать, где это будет правдой, это если a/b должно быть между -1 и 1, но это не так, потому что a и b получены из расчетов, которые делают их очень чувствительными к ошибкам с плавающей запятой.

Например, если вы попытаетесь вычислить b = 2-2*cos(theta) и a = theta*theta для тета в радианах, около нуля, вы столкнетесь с проблемами, потому что вычисление b включает в себя вычитание чисел, очень близкое к 1, даже если математически ответ очень близок к 1,0 для малых углов. (Если я выполню вычисление a / b для theta = 0.4e-7 с использованием движка Spidermonkey Javascript, я получу 1.029, даже если это действительно должно быть чертовски близко к 1.0)

280Z28 - это то, где вы хотите быть, однако, не зная больше о том, как получены a и b, и при каких условиях вы получаете нежелательные результаты, трудно сказать, как лучше всего обеспечить, чтобы вычисления давали значимые числа.

0 голосов
/ 08 октября 2009

Если вы действительно хотите проверить свою теорию о том, что это проблема точности (хотя другие ответы показывают, что вы не можете получить результаты, которые вы получаете), вам следует использовать GMP. Вы уже сказали, что «делите на число, которое слишком мало, меньше точности машины». Привязки GMP C ++ имеют арифметические операторы для всего и на самом деле приятны в использовании по сравнению с привязками C, дают им попытку !

      mpf_class f(1.5);        // default precision
      mpf_class f(1.5, 500);   // 500 bits of precision (at least)
      double out = f.get_d();  // get a double out of an mpf_class
0 голосов
/ 08 октября 2009

Меня немного смущает ваш вопрос, но если вы предполагаете, что -1 <= a / b <= 1 справедливо для всех действительных значений a и b, это определенно не тот случай. Рассмотрим: </p>

1 / 0,5 = 2

Если вы хотите проверить, будет ли деление в пределах [-1, 1], почему бы просто не сделать деление, а затем действовать так, как вы хотите, когда оно падает между -1 и 1.

...