lStoilov
lStoilov

Reputation: 1339

Issues with floating numbers in for loop in PHP

I am trying to create a script that cleans a string from anything that is not a number or operator and then performs the calculation.

It works fine if, for example, the sting is How much is 25 + 35 then * 8 and then / 2, but if the string is How much is 25.5 + 35.5 then * 8 and then / 2, the result is wrong, as it does not consider the float in the numbers

I have tried using is_float in the for loop but without success.

here is a demo http://sandbox.onlinephpfunctions.com/code/c06295bbb567b667cfd65ff0d736330fea0e774b

Any idea what can I do to make it calculate them right?

$result = "How much is 25.5 + 35.5";        
$allowed   = array('1','2','3','4','5','6','7', '8', '9','0','-','+','/','*','.');

$regex  = sprintf('/[^%s]/u', preg_quote(join($allowed), '/'));
$result = preg_replace($regex, '', $result);    
$str = preg_replace('/\s+/', '', $result); 

//Create calculation

$number = array();
$z = 0;
for ($i = 0; $i < iconv_strlen($str); $i++) {
    if (is_numeric($str[$i])) {
        $number[$z] .= $str[$i];
    } else {
        $z++;
        $number[$z] = $str[$i];
        $z++;
    }
};

for ($i = 0; $i < count($number); $i++) {
    $number[$i] = (int) $number[$i];
    $i++;
    $number[$i] = (string) $number[$i];
}

$res = $number[0];

for ($i = 0; $i < count($number); $i++) {
    if ($number[$i+1] === '+') {
        $res += $number[$i+2];
    }elseif($number[$i+1] === '-'){
        $res -= $number[$i+2];
    }elseif($number[$i+1] === '*'){
        $res *= $number[$i+2];
    }elseif($number[$i+1] === '/'){
        $res /= $number[$i+2];
    }
    $i++;
}

echo round($res,2);

Upvotes: 0

Views: 89

Answers (2)

Lenny4
Lenny4

Reputation: 1668

Maybe you should just use eval:

<?php
        //Enter your code here, enjoy!
$result = "How much is 25.5 + 35.5";        
$allowed   = array('1','2','3','4','5','6','7', '8', '9','0','-','+','/','*','.');

$regex  = sprintf('/[^%s]/u', preg_quote(join($allowed), '/'));
$result = preg_replace($regex, '', $result);

eval('$calculation = '.$result.';');
var_dump($calculation);

http://sandbox.onlinephpfunctions.com/code/ecbf77e8bed6c7d7bed93f97bde67ac48f46cff6

Edit

I just found this package: https://github.com/dbojdo/eval-math

Which solve the security problem for eval.

But your user will still have to write (25.5+35.5) * 8 / 2 if you want to perform the addition first.

Upvotes: 0

user3783243
user3783243

Reputation: 5224

The issue is you are iterating over every character rather than keeping them together. You have:

Array
(
    [0] => 25
    [1] => .
    [2] => 5
    [3] => +
    [4] => 35
    [5] => .
    [6] => 5
    [7] => 
)

So:

if ($number[$i+1] === '+') {
   $res += $number[$i+2];

is only matched once at index 3 then index 4's value is taken (e.g. 35) for the addition to 25 (index 0). The decimal values are ignored entirely.

I would use an approach like this:

$result = "How much is 25.5 + 35.5";        
$allowed   = array('1','2','3','4','5','6','7', '8', '9','0','-','+','/','*','.');
$regex  = sprintf('/([%s]+)/u', preg_quote(join($allowed), '/'));
preg_match_all($regex, $result, $match);
switch($match[0][1]){
    case '-':
        echo $match[0][0] - $match[0][2];
    break;
    case '*':
        echo $match[0][0] * $match[0][2];
    break;
    case '+':
        echo $match[0][0] + $match[0][2];
    break;
    case '/':
        echo $match[0][0] / $match[0][2];
    break;
}

https://3v4l.org/Ug20p

Upvotes: 1

Related Questions