Запрос PostgreSQL, возвращающий ноль строк с полем / функцией двойной точности - PullRequest
2 голосов
/ 18 декабря 2011

У меня есть:

SELECT x(point), y(point) WHERE x(point) = 3.69334468807005

x и y имеют тип с двойной точностью.

Я вижу, что это значение действительно находится в таблице, однако выполнение запроса в PostgreSQL делаетничего не вернуть.Почему это может быть так?Может быть, из-за проблем с точностью?

Спасибо!

Ответы [ 3 ]

7 голосов
/ 18 декабря 2011

При работе с числами с плавающей запятой (одинарной или двойной точности) точное сравнение бесполезно в 99% случаев.Это верно не только для PostgreSQL, но и для всех компьютерных языков, использующих арифметику FP.

Три причины заключаются в том, что внутреннее представление двойного числа может содержать намного больше цифр, чем отображается, и в то же время многие числа не могут быть точно выражены с использованием FP (часто цитируемый пример - 0,1), и поэтомувсе «отображаемые» значения усекаются до чего-то, что человек может понять (т.е. ничего подобного «0,099999999999999999999999999» вместо «0,1»).

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

where x between 3.69334468807004 and 3.69334468807006 -- note the different numbers

. Единственные допустимые случаи для прямого сравнения - это случаи, когда значение было только что скопировано ранее.Примером может быть:

SELECT x, y, f1(x,y), f2(x,y), ... INTO TEMP temp_xy FROM points;
SELECT * FROM points p JOIN temp_xy t on p.x = t.x and p.y = t.y;

x и y только что скопированы, поэтому их можно использовать в качестве критерия соединения.

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

3 голосов
/ 21 декабря 2011

Ответ старой школы: «Не сравнивайте числа с плавающей запятой исключительно для равенства». (Элементы стиля программирования, Керниган и Плаугер, 1978)

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

Каноническая статья по математике с плавающей точкой: Что должен знать каждый компьютерный специалист об арифметике с плавающей точкой .

В вашем случае вы можете адаптировать функцию относительного различия из этого FAQ по языку C . (Прокрутите вниз, найдите RelDif ().)

2 голосов
/ 18 декабря 2011

Вы, безусловно, можете проверить, если это проблема точности, просто расширьте предложение WHERE вашего оператора, чтобы он был диапазоном, и ужесточайте этот диапазон (добавляя больше точности), пока у вас не будет вашей записи или вы не сможете подтвердить, что она связана с точностью:

SELECT x(point), y(point)
WHERE x(point) > 3.69
  AND x(point) < 3.70

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

Я также видел, как индексы ведут себя плохо, когда задействованы функции.Есть ли в этой таблице какие-либо индексы?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...