Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
PHP. Практика создания Web-сайтов (второе издание). Авторы: Кузнецов М.В., Симдянов И.В. C++. Мастер-класс в задачах и примерах. Авторы: Кузнецов М.В., Симдянов И.В. Самоучитель PHP 5 / 6 (3 издание). Авторы: Кузнецов М.В., Симдянов И.В. Самоучитель MySQL 5. Авторы: Кузнецов М.В., Симдянов И.В. Социальная инженерия и социальные хакеры. Авторы: Кузнецов М.В., Симдянов И.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум MySQL

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: Опять проблемы с округлением :(
 
 автор: ronin80   (14.08.2008 в 15:00)   письмо автору
 
 

Привет всем!

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

Покажу часть тела процедуры по расчёту цен:

DECLARE vmassa float(10,2);
DECLARE vprice decimal(10,4) default 0;
DECLARE vcalc_price decimal(10,4) default 0;
DECLARE vcalc_summa decimal(10,4) default 0;
DECLARE vkoeff float(5,2) default 0;

...

/*отбираем цену по прейскуранту*/
Select price Into vprice
From jevel.diam_prices
Where dr_id=vrazmer and dc_id=vforma
and color=vcolor and defect=vdefect;

...

/*получение коэффициента*/
Select koeff Into vkoeff
FROM central.gruppa_price `gruppa_price`
Where (gruppa=vgruppa) and (pg_id=ppg_id);

...

/*изменяем цену*/
SET vcalc_price=vprice*vkoeff;

...

/*изменение расчётной цены, суммы*/
Update podbor_lines
Set calc_price=round(vcalc_price,2), calc_summa=round(vcalc_price*vmassa,2)
Where pol_id=vpol_id;

Так вот, здесь при расчёте значения для поля calc_price в таблице podbor_lines если получается например в результате перемножения 92.525 то при использовании функции round с округлением до сотых в базу заносится число 92.52, хотя должно занести 92.53

В чём проблема я так понять до сих пор и не могу. В таблице поля calc_price и calc_summa имеют тип decimal(10,2) (я поменял их с типа float на decimal как мне посоветовали в прошлый раз).

Даже проапгрейдил сервер с 5.06 до 5.1.16, не помогло.

Как решить эту проблему и где рыть я не знаю. Завис вопрос с расчётом цен изделий, я в панике. Помогите пожалуйста разобраться, буду очень благодарен.

Заранее спасибо.

  Ответить  
 
 автор: GeorgeIV   (14.08.2008 в 15:16)   письмо автору
 
   для: ronin80   (14.08.2008 в 15:00)
 

а при 92.526 что заносится?

  Ответить  
 
 автор: Trianon   (14.08.2008 в 15:28)   письмо автору
 
   для: ronin80   (14.08.2008 в 15:00)
 

может быть у Вас там не 92.525 а 92.5249 к примеру?
Какие исходные значения чисел?

  Ответить  
 
 автор: ronin80   (14.08.2008 в 15:45)   письмо автору
 
   для: Trianon   (14.08.2008 в 15:28)
 

проследил закономерность:

1, 2, 3, 4, 6, 7, 8, 9 стоящие третьим знаком дают округление как надо - 1, 2, 3, 4 - в меньшую сторону, 6, 7, 8, 9 - в большую

а вот на 5 округляет либо в меньшую сторону, либо в большую

вот пример числа 92.6250, в базу заносится значение 92.62, значения перемножаемых переменных: vcalc_price=487.5000, vmassa=0.19

  Ответить  
 
 автор: Trianon   (14.08.2008 в 15:50)   письмо автору
 
   для: ronin80   (14.08.2008 в 15:45)
 

0.19 - вероятно, чуть меньше на самом деле.

UPD.Ну точно. 0,19 десятичное = 0,00(11000010100011110101) двоичное
Ближайшее двоично представимое число чуть меньше - 0,189999999
А это при умножении на 487,5 даст 92,6249999
Короче, никакого 92,63 тут помине не будет, и MySQL абсолютно прав.

  Ответить  
 
 автор: ronin80   (14.08.2008 в 15:59)   письмо автору
 
   для: Trianon   (14.08.2008 в 15:50)
 

DECLARE vmassa float(10,2);

соответственно и поле в базе содержащее значения массы тоже float(10,2)

как там может оказаться меньшее значение? или это какие то заморочки мускуля?

  Ответить  
 
 автор: ronin80   (14.08.2008 в 16:04)   письмо автору
 
   для: ronin80   (14.08.2008 в 15:59)
 

тут нарыл такое вот:

http://php.ru/mysql/problems.html#problems-with-float (см. пункт A.5.7. Проблемы со сравнением чисел с плавающей точкой)

это к делу не относится?

  Ответить  
 
 автор: Trianon   (14.08.2008 в 16:05)   письмо автору
 
   для: ronin80   (14.08.2008 в 15:59)
 

>соответственно и поле в базе содержащее значения массы тоже float(10,2)
>как там может оказаться меньшее значение? или это какие то заморочки мускуля?
Числа с плавающей точкой хранятся не точно, а приближенно.
Ну не представить 0,19 в виде конечной последовательности битов.
Точно также как одну треть десятичным числом конечной длины точно не записать.

  Ответить  
 
 автор: ronin80   (14.08.2008 в 16:07)   письмо автору
 
   для: Trianon   (14.08.2008 в 16:05)
 

тоже изменить на decimal?

  Ответить  
 
 автор: ronin80   (14.08.2008 в 16:06)   письмо автору
 
   для: Trianon   (14.08.2008 в 15:50)
 

>0.19 - вероятно, чуть меньше на самом деле.
>
>UPD.Ну точно. 0,19 десятичное = 0,00(11000010100011110101) двоичное
>Ближайшее двоично представимое число чуть меньше - 0,189999999
>А это при умножении на 487,5 даст 92,6249999
>Короче, никакого 92,63 тут помине не будет, и MySQL абсолютно прав.

а что делать?

  Ответить  
 
 автор: sim5   (14.08.2008 в 16:42)   письмо автору
 
   для: ronin80   (14.08.2008 в 16:06)
 

mysql_query("UPDATE table SET field=ROUND(field*0.19,2)");
получим 92.62
$n = round(487.5*0.19,2);
получаем 92.63
mysql_query("UPDATE table SET field=$n");

  Ответить  
 
 автор: ronin80   (14.08.2008 в 18:31)   письмо автору
 
   для: sim5   (14.08.2008 в 16:42)
 

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

возникает вопрос - какой выход из данного положения? изменить типы полей участвующих в расчётах с float на decimal?

P.S. по поводу mysql_query - приложение написанов в Delphi, но намёк я понял о чём выше и написал

  Ответить  
 
 автор: Trianon   (14.08.2008 в 18:53)   письмо автору
 
   для: ronin80   (14.08.2008 в 18:31)
 

>возникает вопрос - какой выход из данного положения?
>изменить типы полей участвующих в расчётах с float на decimal?

Вы жалуетесь на то, что 487.5*0.19 не вычисляется правильно.

Если Вы оставите float , то хранить 0.19 Вы никак не сможете. Соответственно, не сможете и вычислить.

Меняйте.

  Ответить  
 
 автор: ronin80   (14.08.2008 в 19:08)   письмо автору
 
   для: Trianon   (14.08.2008 в 18:53)
 

ок, буду пробовать

благо что изменение с float на decimal не требует исправления в приложении

  Ответить  
 
 автор: sim5   (14.08.2008 в 19:08)   письмо автору
 
   для: Trianon   (14.08.2008 в 18:53)
 

Что-то я не вгоняю, почему поле float не сможет хранить значение 0.19 ?

  Ответить  
 
 автор: Trianon   (14.08.2008 в 19:14)   письмо автору
 
   для: sim5   (14.08.2008 в 19:08)
 

>Что-то я не вгоняю, почему поле float не сможет хранить значение 0.19 ?

Попробуйте записать его в двоичной системе счисления

  Ответить  
 
 автор: sim5   (14.08.2008 в 19:19)   письмо автору
 
   для: Trianon   (14.08.2008 в 19:14)
 

CREATE TABLE `tst` (
`id` int(11) NOT NULL auto_increment,
`number` float default NULL,
`name` varchar(60) default NULL,
`date` date default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=20 DEFAULT CHARSET=cp1251 AUTO_INCREMENT=20 ;

INSERT INTO `tst` VALUES (6, 0.19, 'imag', '2008-08-15');

Выполняю:

mysql_query("UPDATE tst SET number=ROUND(number*487.5,2)");

После выполнения:

INSERT INTO `tst` VALUES (6, 92.62, 'imag', '2008-08-15');

Или вы имеете ввиду именно точность вычислений?

  Ответить  
 
 автор: ronin80   (14.08.2008 в 19:17)   письмо автору
 
   для: sim5   (14.08.2008 в 19:08)
 

я думаю тут имелось ввиду что правильно рассчитывать цену не получится из-за типа данных, а не то чтобы хранить в базе не получится, ведь как-то полгода отработали с программой без каких бы то ни было проблем (не считая данного случая)

из своих наблюдений скажу что в процедуре все обявляемые переменные применяемые при расчётах были сначала типа float как и поля в базе, так вот, некорректное округление было повсюду, где оно применялось (с помощью функции round). заменил типы переменных на decimal, ошибка округления осталась только там где в формулах используетя масса (float)

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

  Ответить  
 
 автор: ronin80   (14.08.2008 в 19:19)   письмо автору
 
   для: ronin80   (14.08.2008 в 19:17)
 

интересно только одно - неужели никто не сталкивался с такой проблемой раньше (на форуме таких тем кроме моей нет)? ведь деньги считают практически везде?

  Ответить  
 
 автор: Trianon   (14.08.2008 в 19:56)   письмо автору
 
   для: ronin80   (14.08.2008 в 19:19)
 

по деньгам оба ответа имеют равные права. Они одинаково отличаются от истинного.

  Ответить  
 
 автор: sim5   (14.08.2008 в 20:12)   письмо автору
 
   для: Trianon   (14.08.2008 в 19:56)
 

Округлять все до рублей - ну кому нужны сейчас копейки? :)

  Ответить  
 
 автор: Trianon   (14.08.2008 в 20:15)   письмо автору
 
   для: sim5   (14.08.2008 в 20:12)
 

А рубли нужны?

Можно вообще все деньги в копейках хранить целыми числами.
Проблема от этого не исчезнет.

  Ответить  
 
 автор: sim5   (14.08.2008 в 20:28)   письмо автору
 
   для: Trianon   (14.08.2008 в 20:15)
 

Ну с целыми хлопот меньше будет, правда ни о какой колбасе 300 гр. за 30.57 руб. и речи, быть не может, 31 руб. и точка :))

  Ответить  
 
 автор: sim5   (14.08.2008 в 19:29)   письмо автору
 
   для: ronin80   (14.08.2008 в 19:17)
 

Ну понял я о чем Trianon. Так не только в MySQL, но и РНР, и JS, например, могут давать разный результат при вычислениях, так что и с другой стороны тоже можно ожидать ошибок.

  Ответить  
 
 автор: ronin80   (15.08.2008 в 07:54)   письмо автору
 
   для: sim5   (14.08.2008 в 19:29)
 

Похоже вы оказались правы! Сменил тип полей на decimal и для всех переменных участвующих в расчётах, округление прошло нормально.

Как говорится век живи век учись :) Всем огромное спасибо за помощь!

  Ответить  
 
 автор: sim5   (15.08.2008 в 08:55)   письмо автору
 
   для: ronin80   (15.08.2008 в 07:54)
 

А я то тут причем? :) Я вот вчера серьезно задумался, получал ли я сдачу в магазинах в виде 5.53 руб., например, не вспомнил такого случая. Если копейки, то 10, 20, 30... И цен с единицами копеек тем более не видел. А кто же их тогда считает? :)

  Ответить  
 
 автор: ronin80   (15.08.2008 в 09:46)   письмо автору
 
   для: sim5   (15.08.2008 в 08:55)
 

у нас все цены в долларах, и соответственно центах :)

  Ответить  
 
 автор: AcidTrash   (16.08.2008 в 00:52)   письмо автору
 
   для: sim5   (15.08.2008 в 08:55)
 

>я то тут причем? :) Я вот вчера серьезно задумался, получал ли я сдачу в магазинах в виде >5.53 руб., например, не вспомнил такого случая. Если копейки, то 10, 20, 30...

Возможно сдачу получить и 1 и 5. :)
Особенно в аптеках(точность как в аптеке).

Просто, если имеет место банковские операции, то значения вычисляются аж до 4 символа после запятой.

  Ответить  
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования