Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770
Форумы портала PHP.SU :: Версия для печати :: floor и ceil
Форумы портала PHP.SU » PHP » SQL и Архитектура БД » floor и ceil

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

1. SAD - 06 Августа, 2013 - 14:34:21 - перейти к сообщению
Есть проблемка с дробными значениями чисел, хранящимися в БД.

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

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

Почему так происходит, я думаю нет смысла объяснять, но все же. Число, к которому применяется функция ceil имеет уже вид примерно 3895.0000...001. Вот как это дело полечить?)
2. EuGen - 06 Августа, 2013 - 14:42:59 - перейти к сообщению
Самое простое - отталкиваться от базовой точности чисел с плавающей точкой на Вашей системе.
Приведу пример для положительных чисел:
PHP:
скопировать код в буфер обмена
  1. $fData = 3895 + 1E-12;
  2. $fDelta= 1E-12;
  3. var_dump($fData, min(ceil($fData), ceil($fData-$fDelta)));
3. SAD - 06 Августа, 2013 - 17:09:48 - перейти к сообщению
Опять же поправочка.
$fData = 3895 + 1E-12; на самом деле 3895.0000...001 + 1E-12
4. EuGen - 06 Августа, 2013 - 17:11:43 - перейти к сообщению
Поскольку речь идёт о ceil положительного числа, то нужно делать вычитание, а не сложение (см. пример выше).
В моём примере $fData - это как раз-таки эмуляция .00000..1 (то есть второй раз прибавлять ничего не нужно)
5. SAD - 07 Августа, 2013 - 10:06:29 - перейти к сообщению
А если число имеет вид 3894.9999999999999a ~ 3895

как показывает практика, вместо a может быть любое число. И нужно взять floor и в итоге должно быть 3895.
6. EuGen - 07 Августа, 2013 - 10:15:35 - перейти к сообщению
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)
7. SAD - 07 Августа, 2013 - 10:19:52 - перейти к сообщению
выбор точности зависит от того под windows ли я сижу или под unix подобными? тем более эти действия будут происходить в процедуре БД firebird.
8. EuGen - 07 Августа, 2013 - 10:23:28 - перейти к сообщению
SAD
Зависит от точности представления чисел с плавающей точкой в целевой системе. В Firebird это, если я не ошибаюсь, стандартная точность 1E-14 (то есть следует выбрать 1E-13 в качестве точности).
9. SAD - 07 Августа, 2013 - 10:25:49 - перейти к сообщению
EuGen, спасибо большое за помощь. Буду колдовать, но аккуратно. А то работа с деньгами =)
10. EuGen - 07 Августа, 2013 - 10:27:08 - перейти к сообщению
Работа с деньгами? Использовать строго тип DECIMAL
11. SAD - 07 Августа, 2013 - 10:30:20 - перейти к сообщению
Знаю, что нужно. Но в наследие досталось double precision и ничего с этим уже нельзя сделать. Разве что с нуля все переписать. А это огогогогого.... даже думать страшно.
(Добавление)
Коллега писал кассу для магазина: намучался с 1 копейкой, пока не пришел к типу decimal.

 

Powered by ExBB FM 1.0 RC1