shar
shar

Reputation: 95

How could two equal numbers not be equal in PHP

I have two numbers which are supposed to be equal to return a difference, I doesn't make sense...

The only way to be able to reproduce this problem here I had to base64_encode my arrays,

here is the script:

basically the script will fix numbers like "1 234,5" to "1234.5" and does calculations, but at the ends it returns

            First Number: 4784.47
            Second Number: 4784.47
            Difference: 9.0949470177293E-13

I just don't understand????????


            $aa = 'YTo3OntpOjA7YToxMDp7czoxMToiRGVzY3JpcHRpb24iO3M6MTI6IkvDqWsgc3rDoW1vayI7czo2OiJQZXJpb2QiO3M6MTI6IjA4LjEwLTA4LjEwLiI7czo2OiJBbW91bnQiO3M6MToiMSI7czoxMjoiRHVyYXRpb25UaW1lIjtzOjc6IjA6MDQ6MDAiO3M6MTI6Ik5ldFVuaXRQcmljZSI7czo1OiIzNSwyMCI7czo5OiJOZXRBbW91bnQiO3M6NjoiMTQwLDgwIjtzOjEwOiJWQVRQZXJjZW50IjtzOjM6IjI1JSI7czozOiJWQVQiO3M6NToiMzUsMjAiO3M6MTE6Ikdyb3NzQW1vdW50IjtzOjY6IjE3NiwwMCI7czo4OiJJdGVtQ29kZSI7czo0OiJCTFVFIjt9aToxO2E6MTA6e3M6MTE6IkRlc2NyaXB0aW9uIjtzOjE3OiJCZWxmw7ZsZGkgaMOtdsOhcyI7czo2OiJQZXJpb2QiO3M6MTI6IjA3LjIyLTA4LjExLiI7czo2OiJBbW91bnQiO3M6MjoiMTIiO3M6MTI6IkR1cmF0aW9uVGltZSI7czo3OiIwOjIxOjQzIjtzOjEyOiJOZXRVbml0UHJpY2UiO3M6NToiMTUsMjAiO3M6OToiTmV0QW1vdW50IjtzOjY6IjMzMCwxMCI7czoxMDoiVkFUUGVyY2VudCI7czozOiIyNSUiO3M6MzoiVkFUIjtzOjU6IjgyLDUyIjtzOjExOiJHcm9zc0Ftb3VudCI7czo2OiI0MTIsNjIiO3M6ODoiSXRlbUNvZGUiO3M6ODoiRklYX0ZMQVQiO31pOjI7YToxMDp7czoxMToiRGVzY3JpcHRpb24iO3M6MjM6IkVnecOpYiBtb2JpbCBlZ3lzw6lnw6FyIjtzOjY6IlBlcmlvZCI7czoxMjoiMDcuMjEtMDguMTEuIjtzOjY6IkFtb3VudCI7czoyOiI1MCI7czoxMjoiRHVyYXRpb25UaW1lIjtzOjc6IjE6MDE6MzgiO3M6MTI6Ik5ldFVuaXRQcmljZSI7czo1OiIxNSwyMCI7czo5OiJOZXRBbW91bnQiO3M6NjoiOTM2LDgzIjtzOjEwOiJWQVRQZXJjZW50IjtzOjM6IjI1JSI7czozOiJWQVQiO3M6NjoiMjM0LDIxIjtzOjExOiJHcm9zc0Ftb3VudCI7czo4OiIxIDE3MSwwNCI7czo4OiJJdGVtQ29kZSI7czo4OiJPRkZfRkxBVCI7fWk6MzthOjEwOntzOjExOiJEZXNjcmlwdGlvbiI7czoxOToiVm9kYWZvbmUgZWd5c8OpZ8OhciI7czo2OiJQZXJpb2QiO3M6MTI6IjA3LjE1LTA4LjEyLiI7czo2OiJBbW91bnQiO3M6MjoiMjAiO3M6MTI6IkR1cmF0aW9uVGltZSI7czo3OiIwOjU0OjM3IjtzOjEyOiJOZXRVbml0UHJpY2UiO3M6NToiMTUsMjAiO3M6OToiTmV0QW1vdW50IjtzOjY6Ijc2MywwNSI7czoxMDoiVkFUUGVyY2VudCI7czozOiIyNSUiO3M6MzoiVkFUIjtzOjY6IjE5MCw3NiI7czoxMToiR3Jvc3NBbW91bnQiO3M6NjoiOTUzLDgxIjtzOjg6Ikl0ZW1Db2RlIjtzOjc6Ik9OX0ZMQVQiO31pOjQ7YToxMDp7czoxMToiRGVzY3JpcHRpb24iO3M6MTU6Ik5lbXpldGvDtnppIFNNUyI7czo2OiJQZXJpb2QiO3M6MTI6IjA3LjEzLTA4LjEzLiI7czo2OiJBbW91bnQiO3M6MjoiNDIiO3M6MTI6IkR1cmF0aW9uVGltZSI7czoxOiItIjtzOjEyOiJOZXRVbml0UHJpY2UiO3M6NToiMzAsNDAiO3M6OToiTmV0QW1vdW50IjtzOjg6IjEgMjc2LDgwIjtzOjEwOiJWQVRQZXJjZW50IjtzOjM6IjI1JSI7czozOiJWQVQiO3M6NjoiMzE5LDIwIjtzOjExOiJHcm9zc0Ftb3VudCI7czo4OiIxIDU5NiwwMCI7czo4OiJJdGVtQ29kZSI7czo3OiJTTVNfSU5UIjt9aTo1O2E6MTA6e3M6MTE6IkRlc2NyaXB0aW9uIjtzOjIzOiJTTVMgaMOhbMOzemF0b24ga8OtdsO8bCI7czo2OiJQZXJpb2QiO3M6MTI6IjA3LjI1LTA4LjExLiI7czo2OiJBbW91bnQiO3M6MjoiMTUiO3M6MTI6IkR1cmF0aW9uVGltZSI7czoxOiItIjtzOjEyOiJOZXRVbml0UHJpY2UiO3M6NToiMTUsMjAiO3M6OToiTmV0QW1vdW50IjtzOjY6IjIyOCwwMCI7czoxMDoiVkFUUGVyY2VudCI7czozOiIyNSUiO3M6MzoiVkFUIjtzOjU6IjU3LDAwIjtzOjExOiJHcm9zc0Ftb3VudCI7czo2OiIyODUsMDAiO3M6ODoiSXRlbUNvZGUiO3M6NzoiU01TX09GRiI7fWk6NjthOjEwOntzOjExOiJEZXNjcmlwdGlvbiI7czoyMjoiU01TIGjDoWzDs3phdG9uIGJlbMO8bCI7czo2OiJQZXJpb2QiO3M6MTI6IjA3LjE3LTA4LjEyLiI7czo2OiJBbW91bnQiO3M6MjoiMTUiO3M6MTI6IkR1cmF0aW9uVGltZSI7czoxOiItIjtzOjEyOiJOZXRVbml0UHJpY2UiO3M6NToiMTUsMjAiO3M6OToiTmV0QW1vdW50IjtzOjY6IjE1MiwwMCI7czoxMDoiVkFUUGVyY2VudCI7czozOiIyNSUiO3M6MzoiVkFUIjtzOjU6IjM4LDAwIjtzOjExOiJHcm9zc0Ftb3VudCI7czo2OiIxOTAsMDAiO3M6ODoiSXRlbUNvZGUiO3M6NjoiU01TX09OIjt9fQ==';

            $tt = 'YTozOntzOjE2OiJTdW1tYXJ5SXRlbU5ldHRvIjtzOjg6IjMgODI3LDU4IjtzOjE0OiJTdW1tYXJ5SXRlbVZBVCI7czo2OiI5NTYsODkiO3M6MTc6IlN1bW1hcnlJdGVtQnJ1dHRvIjtzOjg6IjQgNzg0LDQ3Ijt9';

            $a = unserialize(base64_decode($aa));
            $t = unserialize(base64_decode($tt));


            function calculate_call_fees($a,$t){

                $or_item = 0;

                foreach($a as $k => $r) {
                    $or_item += fix_num($r['GrossAmount']); 
                }        

                $br =  fix_num($t['SummaryItemBrutto']);   

                if($br>$or_item){
                    $diff = $br-$or_item;
                } else {
                    $diff = 0;
                }

                echo 'First Number: ' . $br.'<br/>';
                echo 'Second Number: ' . $or_item.'<br />';
                echo 'Difference: ' . $diff.'<br />';

                echo '<hr />';
                echo '<pre>';
                print_r($a);
                echo '</pre>';
                echo '<hr />';
                echo '<pre>';
                print_r($t);
                echo '</pre>';    

            }

            function fix_num($n){
                return floatval(str_replace(Array(" ",","),array("","."),$n));
            }

            calculate_call_fees($a,$t);

Upvotes: 5

Views: 2601

Answers (3)

Amber
Amber

Reputation: 527193

Using "equals" comparison with floating point numbers is dangerous because of floating point limited precision - you're liable to get small differences due to the rounding involved.

Instead, if you want to see if two floating point numbers are "the same", just see if their difference is below a certain threshold:

if( abs($a - $b) < 0.00000001) {
    // a and b are "equal"
}

Upvotes: 4

Slava Petrov
Slava Petrov

Reputation: 350

It appears as this is "Machine epsilon" issue: http://en.wikipedia.org/wiki/Machine_epsilon

Try to compare the difference between them with 0.000001 instead of comparing them directly.

Upvotes: 1

J0HN
J0HN

Reputation: 26951

It is not just PHP. There is a general problem of representing fractional numbers in the computer. It's subject for various types of overflows, underflows, precision issues and so on. PHP's manual shed some light on the topic.

The general rule - if you demand for two 'seem-equal' numbers to be guaranteed equal - don't use floating point data types (float, double), but fixed point (decimal, numeric)

Upvotes: 1

Related Questions