Неточность при работе с дробными числами

Впервые столкнувшись с программированием, хоть на бэкэнде (php), хоть на фронтенде (JavaScript), я столкнулся с проблемой неточности, когда например вместо 2.0 получаешь 1.99999999 или 2.0000001.

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

$a=2.4;

А в памяти сохраняется

2.400000000000000000000000000000000001

или наоборот где то на единичку поменьше...

2.399999999999999999999999999999999999999

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

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

Например, нам нужно перевести рубли в копейки:

один рубль равен 100 копейкам
21.5 рубль равен 2150 копейкам

Теперь о наших "ущербных" числах, нужно получить до 2х знаков после запятой

2.40000000000001

домножаем на 1000
2400.00000000001

округлим по математическим законам, справа ноль, округлится до
2400

Теперь, делим на 10 и округляем в большую сторону
240

На php такая округлялка будет выглядеть вот так

function float2int2($n,$m):int 
{
    $n*=pow(10,($m+1));
    $n = round($n);
    return Ceil($n/10);
}

или так

function float2int2($n,$m):int 
  {
    $n*=pow(10,($m+1));
    return Ceil(round($n)/10);
  }

Нужно все входные количества дробные округлять и цены, вести все операции с целыми числами, а в самом конце можно выводить деленное на 100 форматированное число из копеек в рубли.

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

Показать комментарии