PHP.SU

Программирование на PHP, MySQL и другие веб-технологии
PHP.SU Портал     На главную страницу форума Главная     Помощь Помощь     Поиск Поиск     Поиск Яндекс Поиск Яндекс     Вакансии  Пользователи Пользователи


 Страниц (1): [1]   

> Без описания
SAD Модератор
Отправлено: 06 Августа, 2013 - 14:34:21
Post Id



Постоянный участник


Покинул форум
Сообщений всего: 2508
Дата рег-ции: Май 2009  
Откуда: Днепропетровск, Украина


Помог: 75 раз(а)




Есть проблемка с дробными значениями чисел, хранящимися в БД.

БД firebird - что не суть - то и важно. Есть поле в табличке с типом double precision.

Допустим, я записал в поле значение 38.95. Потом делаю некоторые необходимые манипуляции с этим числом. К примеру: ceil(38.95 * 100). В итоге 3896 =)

Почему так происходит, я думаю нет смысла объяснять, но все же. Число, к которому применяется функция ceil имеет уже вид примерно 3895.0000...001. Вот как это дело полечить?)
 
 Top
EuGen Администратор
Отправлено: 06 Августа, 2013 - 14:42:59
Post Id


Профессионал


Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




Самое простое - отталкиваться от базовой точности чисел с плавающей точкой на Вашей системе.
Приведу пример для положительных чисел:
PHP:
скопировать код в буфер обмена
  1. $fData = 3895 + 1E-12;
  2. $fDelta= 1E-12;
  3. var_dump($fData, min(ceil($fData), ceil($fData-$fDelta)));


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
SAD Модератор
Отправлено: 06 Августа, 2013 - 17:09:48
Post Id



Постоянный участник


Покинул форум
Сообщений всего: 2508
Дата рег-ции: Май 2009  
Откуда: Днепропетровск, Украина


Помог: 75 раз(а)




Опять же поправочка.
$fData = 3895 + 1E-12; на самом деле 3895.0000...001 + 1E-12
 
 Top
EuGen Администратор
Отправлено: 06 Августа, 2013 - 17:11:43
Post Id


Профессионал


Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




Поскольку речь идёт о ceil положительного числа, то нужно делать вычитание, а не сложение (см. пример выше).
В моём примере $fData - это как раз-таки эмуляция .00000..1 (то есть второй раз прибавлять ничего не нужно)


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
SAD Модератор
Отправлено: 07 Августа, 2013 - 10:06:29
Post Id



Постоянный участник


Покинул форум
Сообщений всего: 2508
Дата рег-ции: Май 2009  
Откуда: Днепропетровск, Украина


Помог: 75 раз(а)




А если число имеет вид 3894.9999999999999a ~ 3895

как показывает практика, вместо a может быть любое число. И нужно взять floor и в итоге должно быть 3895.
 
 Top
EuGen Администратор
Отправлено: 07 Августа, 2013 - 10:15:35
Post Id


Профессионал


Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




SAD
Очевидно тогда нужно понимать, не является ли текущее вещественное число на самом деле целым. Сделать это можно так:
PHP:
скопировать код в буфер обмена
  1. function getFloatInt($fData, $fPrecision = 1E-12)
  2. {
  3.    return abs((int)$fData - $fData)<$fPrecision?(int)$fData:$fData;
  4. }
  5.  
  6. $fGt = 100 + 1E-13;
  7. $fLt = 100 - 1E-13;
  8.  
  9. var_dump(getFloatInt($fGt), getFloatInt($fLt));

Проблема с упомянутым a решается выбором соответствующей точности ($fPrecision)


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
SAD Модератор
Отправлено: 07 Августа, 2013 - 10:19:52
Post Id



Постоянный участник


Покинул форум
Сообщений всего: 2508
Дата рег-ции: Май 2009  
Откуда: Днепропетровск, Украина


Помог: 75 раз(а)




выбор точности зависит от того под windows ли я сижу или под unix подобными? тем более эти действия будут происходить в процедуре БД firebird.
 
 Top
EuGen Администратор
Отправлено: 07 Августа, 2013 - 10:23:28
Post Id


Профессионал


Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




SAD
Зависит от точности представления чисел с плавающей точкой в целевой системе. В Firebird это, если я не ошибаюсь, стандартная точность 1E-14 (то есть следует выбрать 1E-13 в качестве точности).


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
SAD Модератор
Отправлено: 07 Августа, 2013 - 10:25:49
Post Id



Постоянный участник


Покинул форум
Сообщений всего: 2508
Дата рег-ции: Май 2009  
Откуда: Днепропетровск, Украина


Помог: 75 раз(а)




EuGen, спасибо большое за помощь. Буду колдовать, но аккуратно. А то работа с деньгами =)
 
 Top
EuGen Администратор
Отправлено: 07 Августа, 2013 - 10:27:08
Post Id


Профессионал


Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




Работа с деньгами? Использовать строго тип DECIMAL


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
SAD Модератор
Отправлено: 07 Августа, 2013 - 10:30:20
Post Id



Постоянный участник


Покинул форум
Сообщений всего: 2508
Дата рег-ции: Май 2009  
Откуда: Днепропетровск, Украина


Помог: 75 раз(а)




Знаю, что нужно. Но в наследие досталось double precision и ничего с этим уже нельзя сделать. Разве что с нуля все переписать. А это огогогогого.... даже думать страшно.
(Добавление)
Коллега писал кассу для магазина: намучался с 1 копейкой, пока не пришел к типу decimal.
 
 Top
Страниц (1): [1]
Сейчас эту тему просматривают: 1 (гостей: 1, зарегистрированных: 0)
« SQL и Архитектура БД »


Все гости форума могут просматривать этот раздел.
Только зарегистрированные пользователи могут создавать новые темы в этом разделе.
Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.
 



Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS  RSS

 
Powered by ExBB FM 1.0 RC1. InvisionExBB