powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / неверное округление
14 сообщений из 14, страница 1 из 1
неверное округление
    #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
неверное округление
    #34845998
Фотография pamir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
банковское — округление половины (N+1 знак = 5) к ближайшему чётному. В этом случае исчезает систематическая ошибка округления при суммировании большого количества чисел.

Еще тут

Видимо в постгресе заложено банковское округление.
...
Рейтинг: 0 / 0
неверное округление
    #34846011
Dan Black
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Выполните запрос и подумайте о точности представления данных (float, real и т.п.)
Код: plaintext
select round( 16 . 5 ::numeric), round( 16 . 5 ::float)
Код: plaintext
1.
----------------------------
 Verba volent, scripta manent 
...
Рейтинг: 0 / 0
неверное округление
    #34846020
admincheg
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
но если руками прописать 1.5 то считает по другому.
это тоже нормально?
...
Рейтинг: 0 / 0
неверное округление
    #34846029
Фотография pamir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
adminchegно если руками прописать 1.5 то считает по другому.
это тоже нормально?Ну значит то, что написано выше уже не мной.
...
Рейтинг: 0 / 0
неверное округление
    #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
неверное округление
    #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
неверное округление
    #34846858
admincheg
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
мдаааа.... из цикла "забавная математика" :)

может открыть отдельную ветку в форуме с Postgres-овскими забавами ? :)
...
Рейтинг: 0 / 0
неверное округление
    #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
неверное округление
    #34847017
admincheg
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Вроде бы как все встало на свои места...
но вот один вопрос остается у меня: в самом верху приведены исходные данные эксперимента и всё такое.
так вот есди я беру из поля значение (1100/100*"r1",) то получается что то неправильное :(
если же вместо "r1" напишу в запросе руками 1.5 - получается как надо...

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

тогда получается результат округления все же сильно зависит вещественных от типов данных ?
...
Рейтинг: 0 / 0
неверное округление
    #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
неверное округление
    #34847495
admincheg
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Спасибо за разъяснения.

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

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

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

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

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

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

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

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

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

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


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