LINUX.ORG.RU

Равенство float-ов


0

0

Вопрос - как наиболее кошерно сравнивать float-ы? Я обычно делал так: [code] if (abs(x - y) < PRECISION) { ... } [/code]

Но возникло подозрение что вместо PRECISION можно пользоваться какой-то встроенной в math.h константой. Или шумы в младших разрядах надо прикидывать индивидуально для каждой операции?

★★★

а какая собственно точность используется? всё до последнего разряда? думается мне, что лишние цифры не надо учитывать

dimon555 ★★★★★
()
Ответ на: комментарий от dimon555

>думается мне, что лишние цифры не надо учитывать

2 my mind в Си округлять не принято - только при выводе результата через printf надо указывать точность. Однако сравнение выпадает из этой схемы. Нет ли какой-нибудь книги по кошерной работе с плавающей точкой?

Absurd ★★★
() автор топика
Ответ на: комментарий от Absurd

s/сравнение/проверка на равенство/

Absurd ★★★
() автор топика

> Но возникло подозрение что вместо PRECISION можно пользоваться какой-то встроенной в math.h константой.

Константы эти есть FLT_EPSILON, DOUBLE_EPSILON или как-то так. Отвечают за машинную точность. Умножишь на сотню-тысячу и уже можно более-менее спокойно использовать. Но...

> Или шумы в младших разрядах надо прикидывать индивидуально для каждой операции?


Лучше прикидывать самому. А то при достаточно большой погрешности метода может случиться, что какой-нибудь итерационный процесс будет у тебя скакать вокруг одного значения, но разность будет слишком большой.

gaa ★★
()
Ответ на: комментарий от gaa

>> Но возникло подозрение что вместо PRECISION можно пользоваться какой-то встроенной в math.h константой.

>Константы эти есть FLT_EPSILON, DOUBLE_EPSILON или как-то так.

ЕМНИП в MSVC++? В man math.h я их че-то не нашел.

Absurd ★★★
() автор топика

C++:
---
#include <limits>

template <typename t1, typename t2>
bool IsEqual(const t1& v1, const t2& v2)
{
    return ::abs(v1 - v2) <=
        max(std::numeric_limits<t1>::epsilon(),
            std::numeric_limits<t2>::epsilon());
};
---

anonymous
()
Ответ на: комментарий от Absurd

>> Константы эти есть FLT_EPSILON, DOUBLE_EPSILON или как-то так.

> ЕМНИП в MSVC++? В man math.h я их че-то не нашел.


Да, похоже что так, т.к. я нагрепать смог только какие-то подозрительные:
c++/4.3/limits: { return __FLT_EPSILON__; }
c++/4.3/limits: { return __DBL_EPSILON__; }
c++/4.3/limits: { return __LDBL_EPSILON__; }
c++/4.3.2/limits: { return __FLT_EPSILON__; }
c++/4.3.2/limits: { return __DBL_EPSILON__; }
c++/4.3.2/limits: { return __LDBL_EPSILON__; }

gaa ★★
()

Компилировать на 32-битной машине (чтобы использовался сопроцессор):

#include <iostream>
#include <math.h>

int main()
{
  double a, b;
  std::cin >> a; // вводить 7
  std::cin >> b; // вводить 3
  double p = pow(1.0 / b, 3.0);
  std::cout << pow(1.0 - (1.0/3.0) * ((a - 1.0) / 2.0), p); // если подсчитать руками, то будет 0
}

Так что всё зависит от используемой формулы :)

anonymous
()
Ответ на: комментарий от gaa

echo | cpp -dM | grep EPS

#define __DEC32_EPSILON__ 1E-6DF
#define __FLT_EPSILON__ 1.19209290e-7F
#define __DEC128_EPSILON__ 1E-33DL
#define __DEC64_EPSILON__ 1E-15DD
#define __LDBL_EPSILON__ 1.08420217248550443401e-19L
#define __DBL_EPSILON__ 2.2204460492503131e-16

anonymous
()
Ответ на: комментарий от anonymous

Этот пример к тому, что если сравнивать полученный результат с нулём, то любой разумный epsilon не поможет (не сделаете же вы "точность" 0.5?)

Поэтому иногда нужно заниматься zero detection.

anonymous
()
Ответ на: комментарий от anonymous

точно.

непонятно, почему сразу привязались к эпсилонам из math.h

эти эпсилоны дают расстояние между соседними точками решетки флоатов в районе 1 -- то есть http://en.wikipedia.org/wiki/Unit_in_the_last_place

Погрешность в пределах 0.5 ulp будет только в элементарных операциях. Погрешность в серии операций, или в трансцендентных функциях может вылезти другой и это зависит от твоей задачи -- как оценить ее.

dilmah ★★★★★
()

Я обычно пишу что-то типа такого: abs(a-b) < PRECISION * (abs(a)+abs(b)). Здесь PRESICION выбирается каждый раз индивидуально, исходя и из числа разрядов, и из сложности выполняемых действий, и из фактически требуемой точности.

imho, ориентироваться на машинную точность нужно только если ты пишешь библиотеку общего назначения. В таких случаях можно провести вдумчивый анализ происходящего и т.п.; иногда можно вообще без PRECISION (скажем, в дихотомии просто пишешь m=(a+b)/2; if (a==m||b==m) { return m; }).

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

alexsaa
()
Ответ на: комментарий от alexsaa

> Я обычно пишу что-то типа такого: abs(a-b) < PRECISION * (abs(a)+abs(b)). Здесь PRESICION выбирается каждый раз индивидуально, исходя и из числа разрядов, и из сложности выполняемых действий, и из фактически требуемой точности.

Ну поменял ты абсюлютную погрешность на что-то похожее на относительную, но ведб от проблемы "промаха" так и не ушёл.

gaa ★★
()
Ответ на: комментарий от gaa

>> ...выбирается каждый раз индивидуально, исходя...

> от проблемы "промаха" так и не ушёл

Не понимаю. От какой проблемы?

alexsaa
()
Ответ на: комментарий от alexsaa

>> от проблемы "промаха" так и не ушёл
> Не понимаю. От какой проблемы?


Берёшь итерационный процесс и условие останова ||x_i - x_{i-1}|| < eps. А потом из-за слишком маленького eps получаешь, что x_i и x_{i-1} скачут по соседним числам, но не сближаются.

gaa ★★
()
Ответ на: комментарий от gaa

А при чем тут итерационные вычисления ? Речь вроде про сравнение на равенство типов float. Тем более что в итерационных вычислениях x_i и x_{i-1} вычисляются по одному алгоритму и ни о какой потере точности речи быть не может.

koTuk
()
Ответ на: комментарий от gaa

> скачут по соседним числам, но не сближаются.

Ну так смотришь N итераций. Если значения перестали сходиться то прекращаешь расчёты и выдаёшь результат. По крайней мере так в школе учили когда про ряды рассказывали.

true_admin ★★★★★
()
Ответ на: комментарий от koTuk

bash-3.2# cat ./8.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(int argc, char *argv[]) {
    double x = atof(argv[1]);
    double y = atof(argv[2]);

    x = sqrt(x);
    x = sin(x);

    y = sqrt(y);
    y = sin(y);

    printf("x = %f\n", x);
    printf("y = %f\n", y);
    

    if(x == y)
	printf("good :)\n");
    else
	printf("trash :(\n");
    return 0;
}

bash-3.2# gcc 8.c -lm
bash-3.2# ./a.out 123456789123456789 123456789123456789
x = -0.346305
y = -0.346305
good :)
bash-3.2# ./a.out 0.0000000123456789 0.0000000123456789
x = 0.000111
y = 0.000111
good :)

koTuk
()
Ответ на: комментарий от koTuk

> А при чем тут итерационные вычисления ? Речь вроде про сравнение на равенство типов float.

А там сравнение не используется, да?

> Тем более что в итерационных вычислениях x_i и x_{i-1} вычисляются по одному алгоритму и ни о какой потере точности речи быть не может.


Вах! Расскажи обязательно это авторам учебников, а то они, бедняги и не знают! Срочно-срочно.

gaa ★★
()
Ответ на: комментарий от gaa

>А там сравнение не используется, да?

Там сравниваются числа полученные в результате одного и того же алгоритма. При равенстве входных параметров ты получишь гарантированный результат равенства на выходе. Если это не так можешь выбросить свой процессор или компилятор или то и другое вместе :) Ну а про потерю точности это конечно я загнул. В любом случае предел итераций ты должен вычислять исходя из возможностей алгоритма и процессора. А вот при сравнении двух казалось бы одинаковых чисел но полученных разным путем могут возникнуть грабли о чем тут и речь.

koTuk
()
Ответ на: комментарий от anonymous

Если это расстояние между городами в метрах - почему нет? :)

KRoN73 ★★★★★
()

Индивидуально для каждой задачи. В математике возникла даже целая область "некорректных" задач (по определению Тихонова). Это когда малые изменения входных данных могут привести к непредсказуемо большим изменениям на выходе (похоже на так называемый "эффект бабочки"). Так что, серебряной пули в виде единой константы для эпсилон нет и не может быть.

З.Ы. Вообще, странно было прочитать такой вопрос от тебя.

dave ★★★★★
()
Ответ на: комментарий от dave

>Вообще, странно было прочитать такой вопрос от тебя.

Я прикидываю погрешность индивидуально, но возникло ощущение что я делаю что-то не то и можно проще. Как-то надо систематизировать "черную магию" траха ради минимальных потерь разрядов.

Absurd ★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.