Why for with float step dont do last iteration? - php

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);

Related

Why is range() not inclusive when given with float range and interval?

The documentation states that the $end of the range is inclusive. And this is the case most of the time, but when both $end and $step are floats, the last value is missing. Why is that?
print_r(range(1, 13, 1));
print_r(range(1, 13, 0.1));
print_r(range(0.1, 1.3, 0.1));
Output:
Array
(
[0] => 1
[1] => 2
// ...
[11] => 12
[12] => 13
)
Array
(
[0] => 0.1
[1] => 0.2
// ...
[119] => 12.9
[120] => 13
)
Array
(
[0] => 0.1
[1] => 0.2
// ...
[10] => 1.1
[11] => 1.2
// 12 => 1.3 is missing
)
The range is inclusive; however, your assumptions about the numbers adding up are incorrect.
0.1 cannot be represented in binary with exact precision. When you use it in a calculation in php, you'll actually get a number that's a little higher or lower. Take a look at the following codepad:
http://codepad.org/MkoWgAA1
<?php
$sum = 1.0 + 0.1 + 0.1;
if ($sum > 1.2) {
print("1.2 > 1.2");
} else if ($sum < 1.2) {
print("1.2 < 1.2");
} else {
print("1.2 == 1.2");
}
Output:
1.2 > 1.2

Converting long strings to decimals with more than 13 places

This function is converting an array which may contain strings to numbers which may or may not have a decimal.
function toNumberArray(array $stringArray) {
print_r ($stringArray);
echo "<hr>";
$n = [];
for ($i = 0; $i < count($stringArray); $i++) {
$n[] = settype ( $stringArray[$i],"float");
$x = (float) $stringArray[$i];
echo $x;
echo "<br>";
}
return $n;
}
print_r (toNumberArray(["1.123456789012345","2.2000000000000002","3.2999999999999998"]));
Results:
Array ( [0] => 1.123456789012345 [1] => 2.2000000000000002 [2] => 3.2999999999999998 )
1.1234567890123
2.2
3.3
Array ( [0] => 1 [1] => 1 [2] => 1 )
Question:
Why is settype not converting the string to a float?
How to convert if there are 14 or more places after the decimal?
It is converting to a PHP float which is a 64-bit IEEE floating point number. You are probably expecting that PHP uses a 32-bit IEEE floating point number for float and a 64-bit IEEE floating point number for a double'. But, in PHPfloatanddouble` are synonyms. (see the link in jorgonor's comment).
You could use PHP's round function to convert the 64-bit number to 6 or 7 decimal points to simulate the effect you are probably looking for. See http://php.net/manual/en/function.round.php

Calculating decimal from most significant bits & least significant bits in PHP

I'm a little stumped here. I have 2 32-bit integers, one for the most significant bits & the other is the least. How do I find the decimal equivalent from those two in PHP?
Update:
I have tried the suggestion from #bwoebi and modified it a little. Here's the code:
$value=get64($msb, $lsb);
function get64($msb, $lsb) {
$count = count($lsb);
for($i=0; $i < $count; $i++) {
$value[$i] = bcadd(bcmul($msb[$i], bcpow(2, 32)), $lsb[$i] > 0?$lsb[$i]:bcadd(bcsub(bcpow(2, 32), $lsb[$i]), 2 << 30)); // $a most significant bits, $b least significant bits
}
}
Output:
msb=Array ( [0] => INTEGER: 0 [1] => INTEGER: 0 )
lsb=Array ( [0] => INTEGER: 143 [1] => INTEGER: 0 )
value=Array ( [0] => 2147483648 [1] => 2147483648 )
The output does not look right...I would have expected 143 as value[0]. Any input?
bcadd(bcmul($a, bcpow(2, 32)), $b >= 0?$b:bcsub(bcpow(2, 32), $b)); // $a most significant bits, $b least significant bits
should do the job. I am using the bc library as there maybe imprecisions when working with normal operators on the 32 bit systems (It would be converted to float).
You can use the BC Math or GNU Multiple Precision extensions to perform arithmetic with values too large for native PHP variables.

Best way to get N numbers from range betwen 2 (big) numbers

Hello I need to get N numbers from range between 2 big numbers, without the start and end numbers.
The (N) numbers must be on equal intervals... I will try to explain with small numbers:
<?php
$rangeStart = 0;
$rangeEnd = 100;
$n = 9;
In this example i need to get 10,20,30,40,50,60,70,80,90
I have try with 'for loop' but it is veeery slow, because I'm using range like 1207083600 ~ 1275512399
Will appreciate any help.
=====
This is what I call slow http://jsfiddle.net/pbF7N/1/
The start and end are timestamps and I need to extract 10 dates...
range() with its optional 3rd parameter to specify the step size...
range(10, 90, 10);
$range = range(10, 90, 10);
print_r($range);
Array
(
[0] => 10
[1] => 20
[2] => 30
[3] => 40
[4] => 50
[5] => 60
[6] => 70
[7] => 80
[8] => 90
)
Something like this maybe:
function nrange($num, $start, $end)
{
$out = array(); $i = 0;
$interval = floor(($end - $start) / ($num + 1));
while ($i++ < $num )
$out[] = $start + $i * $interval;
return $out;
}
Consider first your example case. Your numbers broke up the range [0..100) into 10 equal intervals, [0..10), [10, 20), etc. up to [90..100).
Notice that the number of intervals is $n+1. So you see that each interval is of length ($rangeEnd - $rangeStart) / $n.
Using this information, you can use range to step across $interval numbers at a time, i.e.,
$interval = ($rangeEnd - $rangeStart) / $n;
$range = range($rangeStart, $rangeEnd, $interval);

Split an array into two evenly by it's values

array
1703 => float 15916.19738
5129 => float 11799.15419
33 => float 11173.49945
1914 => float 8439.45987
2291 => float 6284.22271
5134 => float 5963.14065
5509 => float 5169.85755
4355 => float 5153.80867
2078 => float 3932.79341
31 => float 3924.09928
5433 => float 2718.7711
3172 => float 2146.1932
1896 => float 2141.36021
759 => float 1453.5501
2045 => float 1320.74681
5873 => float 1222.7448
2044 => float 1194.4903
6479 => float 1074.1714
5299 => float 950.872
3315 => float 878.06602
6193 => float 847.3372
1874 => float 813.816
1482 => float 330.6422
6395 => float 312.1545
6265 => float 165.9224
6311 => float 122.8785
6288 => float 26.5426
I would like to distribute this array into two arrays both ending up with a grand total (from the float values) to be about the same. I tried K-Clustering but that distributes higher values onto one array and lower values onto the other array. I'm pretty much trying to create a baseball team with even player skills.
Step 1: Split the players into two teams. It doesn't really matter how you do this, but you could do every other one.
Step 2: Randomly switch two players only if it makes the teams more even.
Step 3: Repeat step 2 until it converges to equality.
$diff = array_sum($teams[0]) - array_sum($teams[1]);
for ($i = 0; $i < 1000 && $diff != 0; ++$i)
{
$r1 = rand(0, 8); // assumes nine players on each team
$r2 = rand(0, 8);
$new_diff = $diff - ($teams[0][$r1] - $teams[1][$r2]) * 2;
if (abs($new_diff) < abs($diff))
{
// if the switch makes the teams more equal, then swap
$tmp = $teams[0][$r1];
$teams[0][$r1] = $teams[1][$r2];
$teams[1][$r2] = $tmp;
var_dump(abs($new_diff));
$diff = $new_diff;
}
}
You'll have to adapt that code to your own structures, but it should be simple.
Here's a sample output:
int(20)
int(4)
int(0)
I was using integers from 0 to 100 to rate each player. Notice how it gradually converges to equality, although an end result of 0 is not guaranteed.
You can stop the process after a fixed interval or until it reaches some threshold.
There are more scientific methods you could use, but this works well.
This is extremely simplistic, but have you considered just doing it like a draft? With the array sorted as in your example, Team A gets array[0], Team B gets array[1] and array[2] the next two picks go to Team A, and so on.
For the example you give, I got one team with ~50,000 and the other with ~45,000.

Categories