Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / неверное округление / 14 сообщений из 14, страница 1 из 1
04.10.2007, 10:59
    #34845948
admincheg
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
есть табличка:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
CREATE TABLE "comm1" (
  "id" SERIAL, 
  "r1" REAL NOT NULL, 
  CONSTRAINT "comm1_pkey" PRIMARY KEY("id")
) WITHOUT OIDS;

INSERT INTO comm1 (id, r1) VALUES ( 1 ,  1 . 5 );

и если потом выполнить запрос:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
SELECT 

      1100 / 100 *"r1",
     Round( 1100 / 100 *"r1"),

      1300 / 100 *"r1",
     Round( 1300 / 100 *"r1"),

      1500 / 100 *"r1",
     Round( 1500 / 100 *"r1") 

        
FROM comm1 
WHERE 
	"id"= 1 ;

то получим странные результаты:
автор16.5 -> 16;
19.5 -> 20;
22.5 -> 22

Т.е. правильно округляется через раз.
от клиента это не зависит....
пробовал вместо "r1" REAL -> "r1" DOUBLE PRECISION : результат тот же :(

но если в звапросе вместо поля "r1" руками написать 1.5, то все вычисляется как надо.
но требуется имеено взять поле из базы.
...
Рейтинг: 0 / 0
04.10.2007, 11:11
    #34845998
pamir
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
банковское — округление половины (N+1 знак = 5) к ближайшему чётному. В этом случае исчезает систематическая ошибка округления при суммировании большого количества чисел.

Еще тут

Видимо в постгресе заложено банковское округление.
...
Рейтинг: 0 / 0
04.10.2007, 11:15
    #34846011
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
Выполните запрос и подумайте о точности представления данных (float, real и т.п.)
Код: plaintext
select round( 16 . 5 ::numeric), round( 16 . 5 ::float)
Код: plaintext
1.
----------------------------
 Verba volent, scripta manent 
...
Рейтинг: 0 / 0
04.10.2007, 11:17
    #34846020
admincheg
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
но если руками прописать 1.5 то считает по другому.
это тоже нормально?
...
Рейтинг: 0 / 0
04.10.2007, 11:18
    #34846029
pamir
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
adminchegно если руками прописать 1.5 то считает по другому.
это тоже нормально?Ну значит то, что написано выше уже не мной.
...
Рейтинг: 0 / 0
04.10.2007, 11:21
    #34846041
admincheg
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
написал вот так:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
      1100 / 100 *"r2",
     Round(cast(( 1100 / 100 *"r2") as numeric)),

      1300 / 100 *"r2",
     Round(cast(( 1300 / 100 *"r2") as numeric)),

      1500 / 100 *"r2",
     Round(cast(( 1500 / 100 *"r2") as numeric)) 

результат вполне математически-верный.
никаких подводных камней не будет ?
...
Рейтинг: 0 / 0
04.10.2007, 11:29
    #34846076
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
Думайте
Код: plaintext
SELECT CAST( 100 / 9 *( 9 ::float) AS numeric),  100 ::numeric/ 9 *( 9 ::float)
Код: plaintext
1.
----------------------------
 Verba volent, scripta manent 
...
Рейтинг: 0 / 0
04.10.2007, 14:19
    #34846858
admincheg
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
мдаааа.... из цикла "забавная математика" :)

может открыть отдельную ветку в форуме с Postgres-овскими забавами ? :)
...
Рейтинг: 0 / 0
04.10.2007, 14:42
    #34846937
Cane Cat Fisher
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
Округление ROUND в Postgres не банковское, а обычное арифметическое, и реализовано правильно. В данном же случае на первый план выступает неявное приведение результата арифметической операции к типу исходных операндов. Другими словами, результат деления двух целых чисел приводится к целому. Причем приводится не округлением (ROUND), а усечением (TRUNC).

Например,
100/51.0 = 1.9607843137254902
100/51 = 1

Самое противное, что этот принцип применяется не ко всему выражению в целом, а к каждой паре операндов, с начала вычисления. То есть, для 100/51*51.0 сначала вычисляется 100/51, приводится к целой единице, а потом умножается на 51.0, и получаем 51.0

100/51*51.0 = 51.0

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

100/51.0*51 = 100.0000000000000002

Двойка за пределами значащих разрядов - неизбежное следствие погрешностей машинной арифметики, и легко убирается тем же ROUND.

Поэтому, если есть желание вести вычисления с вещественными числами, лучше сразу привести все аргументы к вещественному типу, а для подавления мусора за пределами значащих цифр использовать округление ROUND.
...
Рейтинг: 0 / 0
04.10.2007, 15:02
    #34847017
admincheg
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
Вроде бы как все встало на свои места...
но вот один вопрос остается у меня: в самом верху приведены исходные данные эксперимента и всё такое.
так вот есди я беру из поля значение (1100/100*"r1",) то получается что то неправильное :(
если же вместо "r1" напишу в запросе руками 1.5 - получается как надо...

разжуйте плизз эту ситуацию
...
Рейтинг: 0 / 0
04.10.2007, 15:05
    #34847025
admincheg
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
или в постгресе 1.5 введенное руками по умолчанию является типом numeric ?

тогда получается результат округления все же сильно зависит вещественных от типов данных ?
...
Рейтинг: 0 / 0
04.10.2007, 16:49
    #34847434
Cane Cat Fisher
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
Да, действительно, все зависит от того, каким типом считается 1.5

SELECT
1100/100*(1.5::numeric),
1100/100*(1.5::float),
ROUND(1100/100*(1.5::numeric)),
ROUND(1100/100*(1.5::float))
;

?column? | ?column? | round | round
----------+----------+-------+-------
16.5 | 16.5 | 17 | 16

Суть в том, что 1.5 не может точно втиснуться в тип float, и в результате получается что-то вроде 16.49999999999..., округляемое до 16.0. А при выведении на экран применяется некое форматирование (не строгое округление), прячущее эти подробности, и сбивающее с толку непосвященных.

То есть при приведении к numeric считает правильно. И по умолчанию 1.5 приводится к numeric (Chapter 10. Type Conversion, 10.3. Functions, Example 10-4. Rounding Function Argument Type Resolution, "...Since numeric constants with decimal points are initially assigned the type numeric..."). В общем-то все логично. Потому что:

"The type numeric can store numbers with up to 1000 digits of precision and perform calculations exactly. It is especially recommended for storing monetary amounts and other quantities where exactness is required".

А про это ваше там float вааще говорят (Chapter 8. Data Types, 8.1.3. Floating-Point Types):

"If you require exact storage and calculations (such as for monetary amounts), use the numeric type instead".

А теперь скажите, что Вы считаете, и почему выбрали float?
...
Рейтинг: 0 / 0
04.10.2007, 17:01
    #34847495
admincheg
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
Спасибо за разъяснения.

Ужо сделал поле "r1" numeric и теперь всё пучком :)

Считаю бабки :(

Выбрал float ибо как то уж больно много там примудростей вокруг numeric понаписано.... засомневался как оно будет работать в прогах когда этот numeric читать из базы буду...
вопщем тлевратное лияние С (ЦЕ) :)

"r1" - это в реальном то написании "Reward" типа процент вознаграждения... и максимум был бы он с двумя знаками после запятой, из этих соображений казалось бы и float-а хватит... ан нет - вона какие хитрости подстерегают :)

а ващще все суммы храню в копейках - целые числа однако. вот только тут при расчете процентов вещественные числа и проскакивают.

Так то вроде бы и фигня полкопейки ту, полкопейки сюда.... пустячок - а неприятно :(

Вот и стал разбираться
...
Рейтинг: 0 / 0
04.10.2007, 19:20
    #34847915
Cane Cat Fisher
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
неверное округление
>>> и максимум был бы он с двумя знаками после запятой, из этих соображений казалось бы и float-а хватит

float - это не только ограничение на количество значащих цифр, но и специфическое внутреннее представление, накладывающее свои ограничения. Какие именно? Попробуйте на бумажке выразить число 0.1 в двоичном виде, (точнее, в представлении с плавающей запятой) - и увидите. Получите бесконечный хвост, точно как при попытке записать 10/3 десятичной дробью. Разумеется, при втискивании в конкретные ячейки этот хвост приходится где-то обрезать, внося погрешность уже при присвоении значения переменной.

>>> а ващще все суммы храню в копейках

в numeric можно хранить и в рублях.
...
Рейтинг: 0 / 0
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / неверное округление / 14 сообщений из 14, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]