php : result of sum array values is wrong - php

I have an array:
$test =array('49'=> '-0','51'=> '-0','50'=> '0','53'=> '-1.69','55'=> '0','57'=> '-2','59'=> '-6','60'=> '-12','65'=> '0','66'=> '0','67'=> '21.69','69'=> '0','70'=> '0','71'=> '0',);
echo "\n".'===== First Method ========';
echo "\n\n".print_r($test);
echo "\n array_sum: ".array_sum($test);
echo "\n\n".'===== Second Method ========';
$total = 0;foreach($test as $value) $total += $value;
echo "\n foreach:".$total."\n";
the result is
gd#gd:~/Desktop$ php test.php
===== First Method ========Array
(
[49] => -0
[51] => -0
[50] => 0
[53] => -1.69
[55] => 0
[57] => -2
[59] => -6
[60] => -12
[65] => 0
[66] => 0
[67] => 21.69
[69] => 0
[70] => 0
[71] => 0
)
1
array_sum: 3.5527136788005E-15
===== Second Method ========
foreach:3.5527136788005E-15
it is wrong, the result should be 0, not 3.5527136788E-15, how to fix it ?

This is just your standard floating point arithmetic precision error.
php -r "echo -1.69 + -2 + -6 + -12 +21.69;"
3.5527136788005E-15%
You can fix it by using ints rather than floats. For example, if you always expect 2 digits of precision, multiply all your numbers by 100, round them off to ints, sum them, and divide by 100.
php -r "echo (-169 + -200 -1200 +2169 + -600) / 100;"
0%

You are doing array_sum with strings. Remove the quotes on the values, or covert them to integers before using array_sum - I imagine it is converting the strings to integers incorrectly - only on my phone so can't check specifics.
Hope this helps.

Why not just give the values as floating point numbers rather than enclosing them in quotes. That would basically make it a string summation and strange result is expected. I think in before PHP version 4.something it used to convert the string to numbers. It might especially be problem with decimal numbers.

This is just an example of floating point imprecision. It's impossible to represent .69 exactly in binary (much like it's impossible to represent 1/3 exactly in decimal).
If you need exact numbers, you can look into using the bcmath php extension.

Related

Format Numbers with possible leading zeroes in PHP

I'm having a hard time to figure out, how to format a range of number correctly.
Lets say I have an array with the following values:
array( 1, 2, 001, 02, 012 );
How can I change the output of each value to fit the following format?
0 => 1
1 => 2
2 => 0.01
3 => 0.2
4 => 0.12
I'm not looking for a loop ( this is just for explanation), just a way to format the number correctly.
This is the way PHP interpret Integer you can see you have no leading zeros on Integer ;-)
var_dump(array( 1, 2, 001, 02, 012 ));
array(5) {
[0]=>int(1)
[1]=>int(2)
[2]=>int(1)
[3]=>int(2)
[4]=>int(10)
}
The only way to solve it is converting it to a String.

Convert number to and from alphanumeric code

Is it possible to convert from numeric to an alphanumeric code like this:
a
b
c
d
..
z
1
2
3
4
..
aa
ab
ac
ad
..
az
a1
a2
a3
a4
..
aaa
aab
aac
aad
..
aaz
aa1
aa2
etc.
I'm trying to convert large numbers to smaller length alphanumeric strings.
Don't know why you want to do this specifically, but try changing the base from 10 to something like 32;
base_convert($number, 10, 32);
Then to convert back
base_convert($number, 32, 10);
As someone else pointed out - for very large numbers this may not work.
If you need to be able to handle very large numbers, check out this link:
How to generate random 64-bit value as decimal string in PHP
You can use base_convert() for changing the base of your number from 10 (decimal) to 36 (26 latin letters plus 10 arabic numerals).
The result will differ from your given example list. You have used the digits abc..xyz012..789, base_convert will use a diffent order 012..789abc..xyz.
// convert decimal to base36
echo base_convert($number_dec, 10 , 36);
// convert base36 to decimal
echo base_convert($number_b36, 36 , 10);
Translation
dec base36
0 0
1 1
...
9 9
10 a
11 b
...
34 y
35 z
36 10
37 11
..
45 19
46 1a
...
1295 zz
1296 100
1297 101
You could use dechex to convert the number to hex
http://php.net/manual/en/function.dechex.php
For example:
1000000 => f4240
1000001 => f4241
1000002 => f4242
1000003 => f4243
1000004 => f4244
1000005 => f4245
1000006 => f4246
1000007 => f4247
1000008 => f4248
1000009 => f4249
1000010 => f424a
1000011 => f424b
1000012 => f424c
1000013 => f424d
1000014 => f424e
1000015 => f424f
1000016 => f4250
1000017 => f4251
1000018 => f4252
1000019 => f4253
1000020 => f4254
To convert back, just use hexdec
http://php.net/manual/en/function.hexdec.php
base64_encode();
and for decode use
base64_decode();
Both dechex() and base_convert() will fail with large numbers. They are limited by the maximum size and precision of int and float types internally used during conversion.
The http://php.net/manual/pt_BR/function.base-convert.php discussion has some nice helper functions (see 2, 3) that can avoid this problem by using BC-functions to do the math. The BC extension can deal with arbitrarily large numbers.

Why for with float step dont do last iteration?

Why does my for loop with a float step (0.1) don't do the last iteration in PHP - any version?
My test code:
$procents_list = [];
for($i = 0.5; $i <= 1.5; $i += 0.1)
{
$procents_list[] = $i;
}
print_r($procents_list);
Iteration from 5 to 15 with step 1 all ok.
Why that print:
Array
(
[0] => 0.5
[1] => 0.6
[2] => 0.7
[3] => 0.8
[4] => 0.9
[5] => 1
[6] => 1.1
[7] => 1.2
[8] => 1.3
[9] => 1.4
)
Computers use Base-2 (0,1) instead of Base-10 (0,1,2,...9) to represent numbers. In Base-10, some numbers cannot be represented exactly (like 1/3=0.33333). In Base-2, a different set of numbers cannot be represented exactly. In fact, 1/10, which is 0.1 in decimal, has no exact representation in binary. So, math with floats can do weird things and should never be taken as exact values.
I couldn't figure out how to get PHP to print out all the digits without rounding, but writing the same example in Java prints out the following values:
0.5
0.6
0.70000005
0.8000001
0.9000001
1.0000001
1.1000001
1.2000002
1.3000002
1.4000002
So, the value you were getting was slightly larger than 1.5 and the loop thus ended.
In this case, one alternative is to just use integers for the loop and divide by 10 when putting them in the array:
$procents_list = [];
for ($i = 5; $i <= 15; $i += 1)
{
$procents_list[] = $i / 10;
}
print_r($procents_list);

Floating point test assertion - why do these "identical" arrays fail?

I'm using assertSame() in PHPUnit to compare a database result with expected values. The results are floating point numbers.
PHPUnit returns this message (but I can't spot any differences):
Failed asserting that Array (
'1_1' => 11.111111111111
'1_2' => 33.333333333333
'1_3' => 55.555555555556
'1_4' => 0.0
'1_5' => null
'1_total' => 100.0
) is identical to Array (
'1_1' => 11.111111111111
'1_2' => 33.333333333333
'1_3' => 55.555555555556
'1_4' => 0.0
'1_5' => null
'1_total' => 100.0
)
Why is this failing and what is the correct way to compare an arrays of floating point values?
assertEquals has a $floating_delta argument for this type of cases:
$this->assertEquals($expected_array, $actual_array, '', 0.00001);
PHPUnit docs
The problem is almost certainly floating point precision. In the print_r, only so many digits are shown. If all significant bits were displayed, the situation is probably something like this:
Failed asserting that Array (
'1_1' => 11.1111111111110347
'1_2' => 33.3333333333331678
'1_3' => 55.5555555555562773
'1_4' => 0.0
'1_5' => null
'1_total' => 100.0
) is identical to Array (
'1_1' => 11.1111111111110346
'1_2' => 33.3333333333331679
'1_3' => 55.5555555555562771
'1_4' => 0.0
'1_5' => null
'1_total' => 100.0
)
Every floating point comparison—especially equality—must consider the lack of infinite precision.
if ($var == 0.005) /* just plain wrong! */
if (abs ($var, 0.005) < 0.001) /* more correct */
if (abs ($var, 0.005) < 0.0001) /* maybe more correct, depending on application */
if (abs ($var, 0.005) < 0.0000001) /* possibly more appropriate */
Unless anyone has a better suggestion, I will assume this is a floating point precision error, and follow the PHP manual advice: do not compare floating point numbers directly for equality.
So my own solution is to round the array values before comparison.

How to stop PHP from rounding my barcode numbers

I have some bar code numbers in an array. PHP seems to be rounding the barcodes which start with leading zeros. How do I stop this happening and keep the numbers as they were? Code I am using is below:
$array = array(5032227448124,5060028999989,5010121096504,5060028999996,5016254104864,5016402052788,8422248036986,0000003798720,0000003735503,0000003798713);
echo '<pre>';
print_r($array);
echo '</pre>';
This echos the following, as you can see the last four bar codes which feature leading zeros have been changed and had their leading zeros removed. These numbers are always 13 digits long and are padded with zeros.
Array
(
[0] => 5032227448124
[1] => 5060028999989
[2] => 5010121096504
[3] => 5060028999996
[4] => 5016254104864
[5] => 5016402052788
[6] => 8422248036986
[7] => 31
[8] => 1030979
[9] => 31
[10] => 1031004
)
You need to quote them as strings if they arent a number (integer, float, exponent).
The obvious, easy, and also likely wrong answer is to make them strings.
The better answer is to use printf()/sprintf() to pad with zeroes:
printf('%013d', 12345); // output: 0000000012345
MySQL also has a handy LPAD() function:
SELECT LPAD(12345, 13, 0) // output 0000000012345
Here's an easy way to convert your values to padded strings:
$array = array_map(function ($e){return str_pad($e, 13, "0", STR_PAD_LEFT);}, $array);
In the end I just needed to put double quotes around each barcode number e.g.
$array = array("5032227448124","5060028999989","5010121096504","5060028999996","5016254104864","5016402052788","8422248036986","0000003798720","0000003735503","0000003798713");

Categories