PHP: number_format rounding - php

Hi I've been having a problem rounding numbers to -0 instead of just a 0
code:
<?php
$num= 0.000000031;
$round = number_format((float)$num, 1, '.', '');
echo $round * -1;
?>
output:
-0
expected output:
0
I've been looking to any solution but nothing found.
kindly explain & help me why it rounds up to -0 instead of 0? thank you

Not the rounding makes it -0.
The $round variable contains this before the last line:
string(3) "0.0"
You can verify this with adding this line:
var_dump($round);
before the echo.
So if you multiply "0.0" (string) with -1 then the result will be "-0"
Because (string)0 is casted to (float)0 before the multiplication and
(float)0 * -1 = -0
php5 -r 'var_dump((float)0*-1);'
float(-0)
Which is completely normal based on the floating numbers behaviour. (More details: http://en.wikipedia.org/wiki/Signed_zero )
If this is a problem you can add 0 to avoid this "magic":
php5 -r 'var_dump((float)0*-1+0);'
float(0)

Since number_format returns string, you need to cast it to get expected result.
<?php
$num= 0.000000031;
$round = number_format((float)$num, 1, '.', '');
echo (int)$round * (-1); //print 0
?>
PHP Sandbox

You PHP code it's disorganized.
I assume the var $xx on the second line it's $num.
Then, you must first do all operations (operation layer) and then do presentations (presentation layer):
<?php
// Operation layer
$num = 0.000000031;
$round = $num * -1;
// Presentation layer
echo number_format($round, 1, '.', '');
When you do a number_format retrieve a string, not a number.

Related

How to get number of digits in both right, left sides of a decimal number

I wonder if is there a good way to get the number of digits in right/left side of a decimal number PHP. For example:
12345.789 -> RIGHT SIDE LENGTH IS 3 / LEFT SIDE LENGTH IS 5
I know it is readily attainable by helping string functions and exploding the number. I mean is there a mathematically or programmatically way to perform it better than string manipulations.
Your answers would be greatly appreciated.
Update
The best solution for left side till now was:
$left = floor(log10($x))+1;
but still no sufficient for right side.
Still waiting ...
To get the digits on the left side you can do this:
$left = floor(log10($x))+1;
This uses the base 10 logarithm to get the number of digits.
The right side is harder. A simple approach would look like this, but due to floating point numbers, it would often fail:
$decimal = $x - floor($x);
$right = 0;
while (floor($decimal) != $decimal) {
$right++;
$decimal *= 10; //will bring in floating point 'noise' over time
}
This will loop through multiplying by 10 until there are no digits past the decimal. That is tested with floor($decimal) != $decimal.
However, as Ali points out, giving it the number 155.11 (a hard to represent digit in binary) results in a answer of 14. This is because as the number is stored as something like 155.11000000000001 with the 32 bits of floating precision we have.
So instead, a more robust solution is needed. (PoPoFibo's solutions above is particularly elegant, and uses PHPs inherit float comparison functions well).
The fact is, we can never distinguish between input of 155.11 and 155.11000000000001. We will never know which number was originally given. They will both be represented the same. However, if we define the number of zeroes that we can see in a row before we just decide the decimal is 'done' than we can come up with a solution:
$x = 155.11; //the number we are testing
$LIMIT = 10; //number of zeroes in a row until we say 'enough'
$right = 0; //number of digits we've checked
$empty = 0; //number of zeroes we've seen in a row
while (floor($x) != $x) {
$right++;
$base = floor($x); //so we can see what the next digit is;
$x *= 10;
$base *= 10;
$digit = floor($x) - $base; //the digit we are dealing with
if ($digit == 0) {
$empty += 1;
if ($empty == $LIMIT) {
$right -= $empty; //don't count all those zeroes
break; // exit the loop, we're done
}
} else {
$zeros = 0;
}
}
This should find the solution given the reasonable assumption that 10 zeroes in a row means any other digits just don't matter.
However, I still like PopoFibo's solution better, as without any multiplication, PHPs default comparison functions effectively do the same thing, without the messiness.
I am lost on PHP semantics big time but I guess the following would serve your purpose without the String usage (that is at least how I would do in Java but hopefully cleaner):
Working code here: http://ideone.com/7BnsR3
Non-string solution (only Math)
Left side is resolved hence taking the cue from your question update:
$value = 12343525.34541;
$left = floor(log10($value))+1;
echo($left);
$num = floatval($value);
$right = 0;
while($num != round($num, $right)) {
$right++;
}
echo($right);
Prints
85
8 for the LHS and 5 for the RHS.
Since I'm taking a floatval that would make 155.0 as 0 RHS which I think is valid and can be resolved by String functions.
php > $num = 12345.789;
php > $left = strlen(floor($num));
php > $right = strlen($num - floor($num));
php > echo "$left / $right\n";
5 / 16 <--- 16 digits, huh?
php > $parts = explode('.', $num);
php > var_dump($parts);
array(2) {
[0]=>
string(5) "12345"
[1]=>
string(3) "789"
As you can see, floats aren't the easiest to deal with... Doing it "mathematically" leads to bad results. Doing it by strings works, but makes you feel dirty.
$number = 12345.789;
list($whole, $fraction) = sscanf($number, "%d.%d");
This will always work, even if $number is an integer and you’ll get two real integers returned. Length is best done with strlen() even for integer values. The proposed log10() approach won't work for 10, 100, 1000, … as you might expect.
// 5 - 3
echo strlen($whole) , " - " , strlen($fraction);
If you really, really want to get the length without calling any string function here you go. But it's totally not efficient at all compared to strlen().
/**
* Get integer length.
*
* #param integer $integer
* The integer to count.
* #param boolean $count_zero [optional]
* Whether 0 is to be counted or not, defaults to FALSE.
* #return integer
* The integer's length.
*/
function get_int_length($integer, $count_zero = false) {
// 0 would be 1 in string mode! Highly depends on use case.
if ($count_zero === false && $integer === 0) {
return 0;
}
return floor(log10(abs($integer))) + 1;
}
// 5 - 3
echo get_int_length($whole) , " - " , get_int_length($fraction);
The above will correctly count the result of 1 / 3, but be aware that the precision is important.
$number = 1 / 3;
// Above code outputs
// string : 1 - 10
// math : 0 - 10
$number = bcdiv(1, 3);
// Above code outputs
// string : 1 - 0 <-- oops
// math : 0 - INF <-- 8-)
No problem there.
I would like to apply a simple logic.
<?php
$num=12345.789;
$num_str="".$num; // Converting number to string
$array=explode('.',$num_str); //Explode number (String) with .
echo "Left side length : ".intval(strlen($array[0])); // $array[0] contains left hand side then check the string length
echo "<br>";
if(sizeof($array)>1)
{
echo "Left side length : ".intval(strlen($array[1]));// $array[1] contains left hand check the string length side
}
?>

PHP : The awk subtraction giving exponential values

I am getting awk result when I am subtracting two values, the error is I am getting exponent value 2.7755575615629E-17 instead of 0. Anything I am missing to apply, please suggest. These is happening with some cases only like 0.66, 0.67, 0.33,
The prototype of the code I am using is given below,
$_SESSION['x'] = 1;
$_SESSION['x'] = $_SESSION['x'] - 0.83;
echo ( $_SESSION['x']- 0.17) ;
echo '<br>';
But on reversing the values It all fine with 0
$_SESSION['x'] = 1;
$_SESSION['x'] = $_SESSION['x'] - 0.17;
echo ( $_SESSION['x']- 0.83) ;
echo '<br>';
This is because its the floating point numbers. And as per the manual
"The size of a float is platform-dependent, although a maximum of ~1.8e308 with a precision of roughly 14 decimal digits is a common value (the 64 bit IEEE format). "
http://php.net/manual/en/language.types.float.php
Now there are 2 things which could be done by using the type cast your result to (int) or round up the result.
The other option is to use the sprintf
Here is an example
$a = 0.00001234;
echo $a ;
The output will be as
1.234E-5
Now if we do
echo (int)$a ;
The output is 0
or
echo round($a) ;
output will be 0
And finally if we do
echo sprintf('%f', $a);
We will get 0.000012
It is a common problem in computer languages - float values aren't represented exactly. See also http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems. If you have a particular amount of decimal places you want to exactly calculate with, you can use the bcmath functions in PHP:
$_SESSION['x'] = 1;
$_SESSION['x'] = bcsub($_SESSION['x'], 0.83, 10);
echo bcsub($_SESSION['x'], 0.17, 10);
echo '<br>';
Otherwise you can simply use your calculation and add an round($result, $numberOfDecimalPlaces) to you calculated result.

Error With Using (int) and (double) together to Cut off Decimals

When I am using (int) with (double) some times it is not working correct.
Look At The PHP Code Example:
I Need To LEAVE 2 Decimals And REMOVE Other...
I Know number_format(); function But I Cannot Use It. Because It Is Rounding Number
number_format(24.299,2);
Output: 24.30
I Need: 24.29
<?php
$str="158.2";
echo (double)$str; // Output: 158.2
echo (double)$str*100; // Output: 15820
echo (int)((double)$str*100); // Output: 15819 <-WHY? It Must To Be 15820, Why 15819?
echo ((int)((double)$str*100)/100); // Output: 158.19
?>
I need To leave two decimals in the number and cut other WITHOUT rounding.
Because of floating point precision (see for example this question: PHP - Floating Number Precision), 158.2 * 100 is not exactly 15820 but something like 15819.99999999.
Now (int) is for type conversion, not for rounding, and any digits after the point are cut of.
I need To leave two decimals in the number and cut other WITHOUT rounding.
This is easy:
number_format($str, 2);
Update
number_format does round, so it is a bit more complicated:
bcmul($str,100,0)/100
bcmul multiplies with arbitrary precision, in this case 0. Results:
bcmul(158.2,100,0)/100 == 158.2
bcmul(24.299,100,0)/100 == 24.29
This doesn't answer the question of why that happens (it could be a precision bug), but to solve your problem, try using $foo = sprintf("%.2f", (float)$str);.
Example:
$str = "158.2";
$num = (double)$str;
print sprintf("%.2f", $num);
EDIT: Infact, yes, this is a precision issue. (in C++) by printing 158.2 to 20 decimal places, I get the output of "158.19999999999998863132". This is an inherent problem with floating point/double precision values. You can see the same effect by using echo sprintf("%.20f", $var); in PHP.
First off, PHP is a language that allows you to type juggle. Which means you do not need the (int) or the (double) to do what you're trying to do.
<?php
$str="158.2"; //could also do $str = 158.2
echo $str; // Ouput: 158.2
echo $str * 100; //Output: 15820
echo number_format($str, 2); //Output: 158.20
echo number_format(($str*100)/100, 2); //Output: 158.20
?>
Use the number_format command to format your numbers how you want.
More here
Never cast an unknown fraction to integers, see the manual on http://www.php.net/manual/en/language.types.integer.php.
(int) ( (0.1+0.7) * 10 ); will result in 7, not 8 as one might expect. Casting from float to integer will always round down - and you may also want to check the operator precedence http://php.net/manual/en/language.operators.precedence.php.
Solution: calculate your fraction before you cast it. $fStr = (float) $str; $iStr = (int) $fStr;
Fixed.
function cutDecimals($number,$decimal){
$_str=(string)$number;
if(strpos($_str,".")!==false){
$dotPosition=strpos($_str,".")+1;
$_numCount=strpos($_str,".");
$_decimal=strlen($_str)-$dotPosition;
if($_decimal<$decimal) return (double)$_str;
else return (double)substr($_str,0,$_numCount+$decimal+1);
}else return (double)$_str;
}
echo cutDecimals("158.099909865",2)."<br />";
echo cutDecimals("14.02",2)."<br />";
echo cutDecimals("41.12566",2)."<br />";
echo cutDecimals("1.981",2)."<br />";
echo cutDecimals("0.4111",2)."<br />";
echo cutDecimals("144.2",2)."<br />";
echo cutDecimals("55.000000",2)."<br />";
echo cutDecimals("1456115.499811445121",2)."<br />";
?>

Unexpected round() return

I am trying to round down a number using PHP's round() function. Here is the code I am using:
$line_item_price = 13.775;
echo round($line_item_price, 2, PHP_ROUND_HALF_DOWN);
Now when I run the code like this I am hoping to get the output 13.77, except I am getting 0 (or nothing -- not sure which yet).
Now when I remove the PHP_ROUND_HALF_DOWN I get 13.78. Anyone see what I am doing wrong here? It seems like this should be working correctly.
The mode parameter was introduced in version 5.3, therefore it will not work for you. You'll have to find a custom function to do what you are looking for.
You are using a function that is not yet available in your current version of PHP. One way to solve this problem is using the floor function.
$line_item_price = 13.775;
echo floor($line_item_price * 100) / 100;
What I'm doing here is too first multiply the value with 100 and then floor the value. This will give you a rounded down value with the precision of 2. Then to get the correct value you need to devide with 100.
The number 100 comes from the power(10, desired precision)
can you not just do:
echo round($line_item_price, 2)
?
I'm not 100% sure but I think the ROUND_HALF_DOWN etc are for fractions such as 1.5, 2.5 and integers.
Here is a way to do it:
$num = 13.775;
$tmp = intval($num*1000);
$dec = $tmp % 10;
if ($dec > 5) {
$rounded = (1+intval($tmp/10))/100;
} else {
$rounded = intval($tmp/10)/100;
}
echo $rounded,"\n";
This gives : 13.77 for $num=13.775 and 13.78 for $num=13.776
Actually, I'm kind of surprised that it works at all, since the number 13.775 is not exactly representable in floating point:
$ php -r 'printf("%.40f\n", 13.775);'
13.7750000000000003552713678800500929355621
Indeed, it seems that round() is a bit lax about what counts as "half":
$ php -r 'echo round(13.77500000000001, 2, PHP_ROUND_HALF_DOWN) . "\n";'
13.77
Anyway, if your PHP doesn't support PHP_ROUND_HALF_DOWN, here's a simple kluge that gives approximately the same functionality:
function round_half_down ( $num, $digits ) {
$mul = pow( 10, $digits );
return ceil( $num * $mul - 0.5 ) / $mul;
}
This does turn out to work as one would naively expect, but is slightly stricter than round(): round_half_down(13.775, 2) == 13.77, but round_half_down(13.77500000000001, 2) == 13.78. Also, as a curious edge case, round_half_down(0.001, 2) returns -0. If you don't like that, you can always pass the return value through sprintf("%.{$digits}F") to format it nicely.

How to delete all numbers after point in PHP

example: 1.123 =>1 1.999 => 1
thanks.
$y = 1.235251;
$x = (int)$y;
echo $x; //will echo "1"
Edit:
Using the explicit cast to (int) is the most efficient way to to this AFAIK. Also casting to (int) will cut off the digits after the "." if the number is negative instead of rounding to the next lower negative number:
echo (int)(-3.75); //echoes "-3";
echo floor(-3.75); //echoes "-4";
floor()
will round a number down to the nearest integer.
EDIT: As pointed out by Mark below, this will only work for positive values, which is an important assumption. For negative values, you'd want to use ceil() -- but checking the sign of the input value would be cumbersome and you'd probably want to employ Mark's or TechnoP's (int) cast idea instead. Hope that helps.
You could use a bitwise operator.
Without:
echo 49 / 3;
>> 16.333333333333
With "| 0" bitwise:
echo 49 / 3 | 0;
>> 16
$y = 1.234;
list($y) = explode(".", "$y");
If your input can only be positive floats then as already mentioned floor works.
floor(1.2)
However if your integer could also be negative then floor may not give you what you want: it always rounds down even for negative numbers. Instead you can cast to int as another post mentioned. This will give you the correct result for both negative and positive numbers.
(int)-1.2
To remove all number after point use some php function
echo round(51.5); // Round the number, return 51.
echo floor(51.5); // Round down number, return 51.
echo ceil(51.3); // Round up number, return 52.

Categories