I try to calculate growth but if I have like last month value zero then it gives me:
'NaN'
All these values gives me 'NaN':
Examples what can be my values:
var_dump((($this_month - $last_month) / $last_month) * 100);
var_dump(((0 - 0) / 0) * 100);
var_dump(((5 - 0) / 0) * 100); //this should be 100% not 'NaN'
Doing something wrong or my math calculation is wrong?
I used this first accepted answer (The percentage increase ...):
https://stackoverflow.com/questions/5799055/calculate-percentage-saved-between-two-numbers
I have negative percentages also if last month was successful and current month isnt successful.
x/0 is undefined, no matter what x is.
If your $last_month == 0, you're dividing by zero, which mathematically is undefined.
NaN stands for "Not a Number", i.e. undefined.
The response your code is giving you is correct.
Related
I am trying to figure out if there is a way to find the percentage between 2 numbers.
It's a progress / ranking system.
I want to find the percentage the $current_exp is between the $current_min and the $current_max values, is there a way to achieve this in PHP? So far I've got to this, but it doesn't work as you progress in ranks, it doesn't treat the $current_min as 0 so when you rank up, it says you are like 75% into your next rank progression when you're in fact 0. Does this make sense?
$currentProg = ($current_exp * 100) / $current_max;
Say the current minimum is 18750 and the current maximum is 25100, the current exp is 22000... What percentage from the min to the max is the current exp? This will change each rank as the $current_min and $current_max variables get set depending on the exp of the user.
The next rank is Current min is 25100 Current max is 34230
Currently, when you are at 26000 exp, the output is saying 75.956763073327% which is not correct, it should be like 1 or 2%?
Thanks in advance 🙏
Not a good mathematician, but it looks like it should be:
(Difference of rank - minimum) / (Difference of maximum - minimum) * 100
<?php
$x = 25100;
$z = 34230;
$y = 26000;
echo ($y - $x + 1) / ($z - $x + 1) * 100; // outputs 9.8674843938232 %
Online Demo
Note: + 1 is added to both numerator and denominator to avoid divide by zero errors.
I'm working on code that has to do the following with the result of a calculation:
If the result exceeds the limit that can be represented in PHP's integer type then throw an exception.
If the result doesn't exceed that limit but it did result in a float being generated, issue a warning and round the result to an integer.
I've implemented the following method to do this:
const MAX = PHP_INT_MAX;
const MIN = (PHP_INT_MAX * -1) -1;
private function validateResult ($result)
{
// Check that we still have an integer
if (!is_int ($result))
{
// If the result is out of bounds for an integer then throw an exception
if (($result > static::MAX) || ($result < static::MIN ))
{
// We've gone out of bounds
throw new exception\AmountRangeException ("New value exceeds the limits of integer storage");
}
// If the result can be rounded into an integer then do so and issue
// a warning.
trigger_error ("A non-integer value of $result resulted and has been rounded", E_USER_NOTICE);
$result = (int) round ($result);
}
return $result;
}
However it fails unit testing when attempting to add 1 to PHP_INT_MAX. I tried the following in PHP interactive mode:
php > var_dump (PHP_INT_MAX);
int(9223372036854775807)
php > var_dump (PHP_INT_MAX + 1);
double(9.2233720368548E+18)
php > var_dump ((PHP_INT_MAX + 1) > PHP_INT_MAX);
bool(false)
php > var_dump ((PHP_INT_MAX + 10) > PHP_INT_MAX);
bool(false)
php > var_dump ((PHP_INT_MAX + 100) > PHP_INT_MAX);
bool(false)
php > var_dump ((PHP_INT_MAX + 1000) > PHP_INT_MAX);
bool(false)
php > var_dump ((PHP_INT_MAX + 10000) > PHP_INT_MAX);
bool(true)
So it looks like my detection code will only work if the result goes about 5 orders of magnitude out of range.
As I want sums that generate a float to pass provided the result can be rounded to an integer, simply throwing an exception if the result isn't an int wouldn't meet the requirements.
Is there a reliable way of detecting that a number has exceeded integer range, even by a small amount?
UPDATE: Further investigation shows that the value can go over by up to 1025 before it's actually considered bigger than PHP_INT_MAX.
php > var_dump ((PHP_INT_MAX + 1025) > PHP_INT_MAX);
bool(false)
php > var_dump ((PHP_INT_MAX + 1026) > PHP_INT_MAX);
bool(true)
UPDATE 2: I've implemented a provisional fix, but that fix is really hacky and inelegant so I'm leaving this question open in the hope that someone has a better suggestion.
if ((($result > static::MAX) || (($result == static::MAX) && ((string) $result != (string) static::MAX)))
|| (($result < static::MIN) || (($result == static::MIN) && ((string) $result != (string) static::MIN)))) {}
The idea is that if the numbers are mathematically the same according to a PHP comparison, but they're not the same after the numbers have been cast to string then they must have overflowed, but by less than can be detected with a > or < comparison. This seems to work in unit testing, but I really don't think this is the best solution and am currently constructing a more rigorous set of unit tests to see what happens with values just below the boundary, just above it, or exactly on it.
UPDATE 3: The above approach won't work with negative overflow. If the result triggers a negative overflow the result is a double, but its value is still the same as (PHP_INT_MAX * 1) - 1
php > var_dump ((PHP_INT_MAX * -1) - 1);
int(-9223372036854775808)
php > var_dump ((PHP_INT_MAX * -1) - 2);
double(-9223372036854775808)
Turns out the answer was incredibly simple once I thought of it. All it took was to redefine the MIN and MAX constants to not be the biggest possible positive and negative integer values, but to define them as the biggest values that when the value being tested and the MIN/MAX values are both cast to float, the value under test will still be within the range of MIN/MAX.
Experimentation has shown that making the limits 512 short of the absolute limit achieves this.
const MAX = PHP_INT_MAX - 512;
const MIN = (PHP_INT_MAX * -1) + 512;
Now any value outside that range can be detected regardless of whether a cast to float occurs or not.
There is still some issues with this approach (the backoff zone probably doesn't need to be anything like this big on a 32 bit system) but it's a much more elegant fix than type juggling and string comparisons.
I've got this spot of code that seems it could be done cleaner with pure math (perhaps a logarigthms?). Can you help me out?
The code finds the first power of 2 greater than a given input. For example, if you give it 500, it returns 9, because 2^9 = 512 > 500. 2^8 = 256, would be too small because it's less than 500.
function getFactor($iMaxElementsPerDir)
{
$aFactors = range(128, 1);
foreach($aFactors as $i => $iFactor)
if($iMaxElementsPerDir > pow(2, $iFactor) - 1)
break;
if($i == 0)
return false;
return $aFactors[$i - 1];
}
The following holds true
getFactor(500) = 9
getFactor(1000) = 10
getFactor(2500) = 12
getFactor(5000) = 13
You can get the same effect by shifting the bits in the input to the right and checking against 0. Something like this.
i = 1
while((input >> i) != 0)
i++
return i
The same as jack but shorter. Log with base 2 is the reverse function of 2^x.
echo ceil(log(500, 2));
If you're looking for a "math only" solution (that is a single expression or formula), you can use log() and then take the ceiling value of its result:
$factors = ceil(log(500) / log(2)); // 9
$factors = ceil(log(5000) / log(2)); // 13
I seem to have not noticed that this function accepts a second argument (since PHP 4.3) with which you can specify the base; though internally the same operation is performed, it does indeed make the code shorter:
$factors = ceil(log(500, 2)); // 9
To factor in some inaccuracies, you may need some tweaking:
$factors = floor(log($nr - 1, 2)) + 1;
There are a few ways to do this.
Zero all but the most significant bit of the number, maybe like this:
while (x & x-1) x &= x-1;
and look the answer up in a table. Use a table of length 67 and mod your power of two by 67.
Binary search for the high bit.
If you're working with a floating-point number, inspect the exponent field. This field contains 1023 plus your answer, except in the case where the number is a perfect power of two. You can detect the perfect power case by checking whether the significand field is exactly zero.
If you aren't working with a floating-point number, convert it to floating-point and look at the exponent like in 3. Check for a power of two by testing (x & x-1) == 0 instead of looking at the significand; this is true exactly when x is a power of two.
Note that log(2^100) is the same double as log(nextafter(2^100, 1.0/0.0)), so any solution based on floating-point natural logarithms will fail.
Here's (nonconformant C++, not PHP) code for 4:
int ceillog2(unsigned long long x) {
if (x < 2) return x-1;
double d = x-1;
int ans = (long long &)d >> 52;
return ans - 1022;
}
Is it possible to round a number where if it's .5, just leave it, anything below .5 round down, anything above .5 round up?
For example:
5.0 * 1.35 = 6.75 // leave it
5.2 * 1.35 = 7.02 // round down to 7.00
5.5 * 1.35 = 7.56 // round up to 8.00
I've formatted with round($n,0, PHP_ROUND_HALF_UP) where $n is the product from the above calc , which leaves 6.75 but returns 7.02 for the next one. I also tried round($n,-1, PHP_ROUND_HALF_UP) which gives me the 7.00 on the second calc but then of course won't return a 6.75 for the first, instead it returns 680.
This is a ticket markup calculation where the user enters the first number and is multiplied by the second. I actually remove the decimal because they don't want to see it, and they want this sort of customized rounding on the result.
function myround($num, $prec) {
$rhu = round($num, $prec, PHP_ROUND_HALF_UP);
$rhd = round($num, $prec, PHP_ROUND_HALF_DOWN);
return ($rhu + $rhd) / 2;
}
Works for any precision you like. For hundreth's place, as in the example, $prec would need to be 2.
The only way to determine the value of the last non-zero digit of a given floating point number in PHP is to convert it to a string.
$str = (string) $float;
$result = ($str[strlen($str) - 1] == 5) ? $float : round($float);
Example
Of course, no matter what you do it will be subject to a small margin of error because of the floating point precision issue.
$n = round($n, 2);
if($n % .05 != 0 || $n % .1 == 0)
{
$n = round($n);
}
Does this work for you? I'm assuming the 5 you speak of is the hundredth digit, and if it's not 5 then you want a whole number.
I need to convert pounds to kilograms and vice versa -- and round the number to the nearest quarter (and possibly half). I need to be able to make a conversion, take that conversion and convert it back, and have all the values still be the same.
Sample code:
for ($i = 1; $i <= 100; $i = $i + .25)
{
$kilograms = convert_pounds_to_kilograms($i);
$pounds = convert_kilograms_to_pounds($kilograms);
$new_kilograms = convert_pounds_to_kilograms($pounds);
echo ("$i => $pounds => $kilograms => $new_kilograms<br/>");
}
function convert_pounds_to_kilograms($pounds)
{
assert(is_numeric($pounds) === TRUE);
$kilograms = $pounds * 0.45359237;
// Round to the nearest quarter
$kilograms = round($kilograms * 4, 0) / 4;
return $kilograms;
}
function convert_kilograms_to_pounds($kilograms)
{
assert(is_numeric($kilograms) === TRUE);
$pounds = $kilograms * 2.20462262185;
// Round to the nearest quarter
$pounds = round($pounds * 4, 0) / 4;
return $pounds;
}
The first line of output is correct:
1 => 1 => 0.5 => 0.5
The second is not correct:
1.25 => 1 => 0.5 => 0.5
(the value 1 should have been 1.25)
How do I do this? I'm not looking for precision in the conversion, obviously. I just need to be able to convert these imprecise values back and forth to the same number.
EDIT 1:
The reason for this is that I will be allowing users to enter their height in centimeters, meters, or feet/inches -- then saving whatever their entered value to centimeters (thus, the first conversion). Users can then view their height in either centimeters, meters, or feet/inches (thus, a possible conversion again).
So, say a user enters their height in ft/inches, I need to store that in centimeters. Then the user may want to see that height again in ft/inches -- meaning I need to convert the centimeters back to the original ft/inches value.
Users will probably be limited to entering and viewing values to quarter increments. Meaning, 5'8.25" is valid, but not 5'8.39".
Do not round in the function itself. Go as precise as you can. Only round right before you display it.
If you round it off in the functions, then the ROUNDED value is put into the next function. If you keep doing this, you're going to lose a lot of precision, and you'll get less precise results the more you loop it.
You are rounding to 0 decimal places, hence the 1.25 is rounded to 1.
Try removimg the round() function and see what happens.
http://php.net/manual/en/function.round.php
Edit
To address your comment change:-
$kilograms = convert_pounds_to_kilograms($i);
To:-
$kilograms = round(convert_pounds_to_kilograms($i), 0);
And remove round() from inside your functions.
You're rounding out the precision. Your $pounds that you're printing out are converted from original value ($i) to kilograms with a round function, and then back to pounds with a round function; the round() is causing your values to converge.