PHP adding and subtracting wrong

Asked

Viewed 546 times

1

I have a very strange problem here.

It is a code for cash closing, I have the total value of sales, the user informs sales in credit, debit and cash, if the sum of the 3 is equal to the total sales he lets close the box.

The problem is that pro PHP 211.1 - 211.1 = -2.8421709430404E-14

Print

public function isValid($value, $context = null)
{
    foreach ($context as $key => $valor) {
        $valor = str_replace('.', '', $valor);
        $valor = str_replace(',', '.', $valor);
        $context[$key] = (float)number_format($valor, 2);
    }

    $total = $context['total_pedidos'] - ($context['valor_credito'] + $context['valor_debito'] + $context['valor_dinheiro']);

    var_dump($context);
    var_dump($total);
    die();
    $this->setValue($total);
    if(empty($total)){
        return true;
    }else{
        $this->error(self::FLOAT);
        return false;
    }

}

Another thing, how I used the number_format($valor, 2) it should put 2 decimal places no?

When I play other values it makes the correct account.

I am using PHP 5.5.12

2 answers

1

The problem itself is the operation with floating point numbers. They are not an exact value, but an approximation. This also occurs in any language, not only in PHP. For example in Javascritpt:

a = 31.8;
b = 49.9;
c = 129.4;

total = 211.1;

alert(total - (a + b + c));

We have here a question related to this subject and with great answers (I recommend reading for a better understanding):

Inaccurate result in broken numbers calculation

To solve your problem, you can use the functions of BC Math to make mathematical calculations with floating points accurately:

<?php

$a = '31.8';
$b = '49.9';
$c = '129.4';

$total = '211.1';

bcscale(2);

$subtotal = bcadd($a, $b);
$subtotal = bcadd($subtotal, $c);

var_dump(bcsub($total, $subtotal));

See working.

Or do these operations using integer numbers, multiplying by 100 and at the end dividing everything by 100:

<?php

$context = [
    'total_pedidos' => '211,10',
    'valor_credito' => '31,80',
    'valor_debito' => '49,90',
    'valor_dinheiro' => '129,40'
];

foreach ($context as $key => $valor) {
    $valor = str_replace('.', '', $valor);
    $valor = str_replace(',', '.', $valor);
    $context[$key] = number_format($valor, 2) * 100;
}

$total = $context['total_pedidos'] - ($context['valor_credito'] + $context['valor_debito'] + $context['valor_dinheiro']);
$total = $total / 100;

var_dump($total);

See working.

Other references:

  • It worked with whole numbers, thank you!

-2

Warning:

Although the question is using float to manipulate currency, never store or manipulate money using float, as rounding errors may occur. Work with integers internally, where the integer represents the total number of cents. An alternative is to use the math module Bcmath, that handles arbitrary precision numbers with 100% accuracy.


As for the question you forgot to format the output and cast to an already formatted value.

<?php
function isValid($context = null)
{
    foreach ($context as $key => $valor) {
        $valor = str_replace('.', '', $valor);
        $valor = str_replace(',', '.', $valor);
        // Sem o cast (float) se o fizer você perde a formatação.
        $context[$key] = number_format($valor, 2);
    }

    // Faltou formatar o resultado
    $total = number_format(
               $context['total_pedidos'] - (
               $context['valor_credito'] + 
               $context['valor_debito'] + 
               $context['valor_dinheiro']), 2);

    var_dump($context);
    var_dump($total);


}

$arr = [
'total_pedidos' => '211,1',
'valor_credito' => '31,8',
'valor_debito'  => '49,9',
'valor_dinheiro'=> '129,4'
];

isValid($arr);
?>

Upshot:

array(4) {
  ["total_pedidos"]=>
  string(6) "211.10"
  ["valor_credito"]=>
  string(5) "31.80"
  ["valor_debito"]=>
  string(5) "49.90"
  ["valor_dinheiro"]=>
  string(6) "129.40"
}
string(4) "0.00"

Link to the code.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.