2008-10-11 5 views
4

У меня есть небольшое финансовое приложение с PHP в качестве внешнего интерфейса и MySQL в качестве задней части. У меня есть древние предрассудки, и я храню денежные значения в MySQL как целое число центов. Мои формы HTML позволяют вводить значения доллара, например «156.64», и я использую PHP для преобразования этого значения в центы, а затем я храню центы в базе данных.Преобразование денежной строки PHP в целочисленную ошибку

У меня есть функция, которая как очищает значение доллара от формы, так и конвертирует ее в центы. Я стираю ведущий текст, убираю зацепляющий текст, умножаю его на 100 и преобразую в целое. Это последний шаг

$cents = (integer) ($dollars * 100);

Это прекрасно работает практически во всем, за исключением очень немногих ценностей, как «156.64», который последовательно превращается в 15663 центов за исключением. Почему он это делает?

Если я это сделать:

$cents = (integer) ($dollars * 100 + 0.5);

затем он последовательно работает. Зачем нужно добавлять это значение округления?

Кроме того, мои предрассудки о хранении денежных средств в виде целых чисел, а не значений с плавающей запятой, больше не нужны? Будут ли современные расчеты поплавка производить хорошо округленные и точные денежные значения, достаточные для поддержания 100% точного учета?

ответ

4

Если вам нужна точность, вы должны сохранить свои денежные значения, используя тип данных DECIMAL в MySQL.

2

Ваши «предрассудки» о поплавках никогда не будут преодолены - это имеет фундаментальное значение для их работы. Не вдаваясь в подробности, они сохраняют число, основанное на силе двух, и поскольку не все десятичные числа могут быть представлены таким образом, это не всегда работает. Единственное надежное решение - сохранить номер как последовательность цифр и местоположение десятичной точки (в соответствии с типом DECIMAL, упомянутым выше).

Я не 100% на PHP, но возможно ли, что умножение преобразует ints в float и, следовательно, представляет собой именно ту проблему, которую вы пытаетесь избежать?

2

Значения денег/денег никогда не должны храниться в базе данных (или использоваться в программе) как плавающие.

Ваш цельный метод является точной, поскольку используется DECIMAL, NUMERIC или MONEY тип, если таковой имеется.

Ваша проблема вызвана $ долларами, рассматриваемыми как float, и PHP не имеет лучшего типа для работы с деньгами. В зависимости от того, когда назначается $ доллары, его можно рассматривать как строку или float, но, безусловно, преобразуется в float, если она все еще является строкой для операции * 100, если она похожа на float.

Возможно, вам лучше разобрать строку на целое значение «деньги» самостоятельно (используя регулярное выражение) вместо того, чтобы полагаться на неявные преобразования, которые делает PHP.

2

Casting не round() как в округления до ближайшего, он truncates в десятичной системе: (int)3.99 дает 3. (int)-3.99 - -3.

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

+1

неверный. Броски вокруг. См. Мой ответ – 2010-01-14 12:32:20

2

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

Предполагая, что предыдущий код уже проверен и отформатирован ввод, попробуйте следующее:

list($bills, $pennies) = explode('.', $dollars); 
$cents = 100 * $bills + $pennies; 

Ваше предубеждение против значения с плавающей точкой для представления денег обоснованно из-за усечения и из значений, преобразованные из базы-10, base-2 и обратно.

0

Вместо использования

$ центов = (целое число) ($ долларов * 100);

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

$ центов = bcmul ($ долларов, 100, 2);

0

При преобразовании с поплавка в целое число будет округлено в направлении нуля (src).

Прочитано Floating point precision предупреждение.

0

Нет смысла хранить деньги как целое, если вы вводите его с помощью операции с плавающей запятой (каламбур не предназначен). Если вы хотите конвертировать из string в int и быть в соответствии с вашим «предрассудком», вы можете просто использовать строковые функции.

Вы можете использовать произвольную библиотеку точности для деления на 10 (они обрабатывают числа внутренне как строки), например. bcdiv() или gmp_div_q(), но, конечно, вы могли бы также использовать его с самого начала для всех математических вычислений.

Или вы можете использовать простые строковые функции:

<?php 
// Quick ugly code not fully tested 
$input = '156.64'; 
$output = NULL; 

if(preg_match('/\d+(\.\d+)?/', $input)){ 
    $tmp = explode('.', $input); 
    switch(count($tmp)){ 
     case 1: 
      $output = $tmp[0]; 
      break; 

     case 2: 
      $output = $tmp[0] . substr($tmp[1], 0, 2); 
      break; 

     default: 
      echo "Invalid decimal\n"; 
    } 
}else{ 
    echo "Invalid number\n"; 
} 

var_dump($output); 

?> 
1

Вы никогда не должны когда-либо хранить валюту в плавающей точкой, потому что он всегда получить результаты, которые вы не ожидаете.

Отъезд php BC Maths, он позволяет хранить вашу валюту в виде строки, а затем выполнять арифметику с высокой точностью.

 Смежные вопросы

  • Нет связанных вопросов^_^