I am having a variety of long numbers and I am trying to write a function to format them correctly. Can someone help me out?
I already tried "number_format()" and "round()" but that doesn't solve my problems..
I would like to round it like following:
1024.43 --> 1,024.43
0.000000931540 --> 0.000000932
0.003991 --> 0.00399
0.3241 --> 0.324
1045.3491 --> 1,045.35
So that means, If number is bigger than "0" it should round to 2 decimal places and add thousands seperator (like 6,554.24) AND if number less than "1" it should round to 3 digits whenever numbers appear after the zeros (for example 0.0003219 to 0.000322 OR 0.2319 to 0.232)
EDIT:
The same should apply to "-" values. For example:
-1024.43 --> -1,024.43
-0.000000931540 --> -0.000000932
-0.003991 --> -0.00399
-0.3241 --> -0.324
-1045.3491 --> -1,045.35
Adapting from https://stackoverflow.com/a/48283297/2469308
handle this in two separate cases.
for numbers between -1 and 1; we need to calculate the number of digits to round. And then, using number_format() function we can get the result.
for else, simply use number_format() function with decimal digits set to 2.
Try the following:
function customRound($value)
{
if ($value > -1 && $value < 1) {
// define the number of significant digits needed
$digits = 3;
if ($value >= 0) {
// calculate the number of decimal places to round to
$decimalPlaces = $digits - floor(log10($value)) - 1;
} else {
$decimalPlaces = $digits - floor(log10($value * -1)) - 1;
}
// return the rounded value
return number_format($value, $decimalPlaces);
} else {
// simply use number_format function to show upto 2 decimal places
return number_format($value, 2);
}
// for the rest of the cases - return the number simply
return $value;
}
Rextester DEMO
$x = 123.456;
echo number_format($x, max(2, 3 - ceil(log10(abs($x))))) . "\n";
$x = 1.23456;
echo number_format($x, max(2, 3 - ceil(log10(abs($x))))) . "\n";
$x = 0.0123456;
echo number_format($x, max(2, 3 - ceil(log10(abs($x))))) . "\n";
$x = 0.0000123456;
echo number_format($x, max(2, 3 - ceil(log10(abs($x))))) . "\n";
$x = 0.000000123456;
echo number_format($x, max(2, 3 - ceil(log10(abs($x))))) . "\n";
$x = 0.00000000123456;
echo number_format($x, max(2, 3 - ceil(log10(abs($x))))) . "\n";
Output:
123.45
1.23
0.0123
0.0000123
0.000000123
0.00000000123
Basically this always keeps a minimal of 2 decimal digits up to 3 significant digits.
However, because of the way floating point is handled internally (as power of 2 and not 10), there are some catches. Numbers like 0.1 and 0.001 and so forth can't be stored precisely, so they are actually stored as 0.09999999... or things like that. In cases like this it may seem like it is computing things wrong and give you answers with more significant digits than it should.
You can try to counteract this phenomena by allowing an error margin to the formula:
number_format($x, max(2, 3 - ceil(log10(abs($x))) - 1e-8))
But this may cause other undesirable effects. You will have to make tests.
Related
For example, since 25 and 2.2360679775 (or 5^0.5) are both powers of 5, I want the function to return 5.
The only way I've found is to brute force it and just try every number.
$one=25;
$two=125;
for($b=2;$b<=10;$b++)
{
$x=pow($one,1/$b);
for($c=2;$c<=10;$c++)
{
$y=pow($two,1/$c);
if($y == $x)
{
return $x;
}
}
}
As Mbo points out in the comments, every number (z) is the power some number (x) to some other number (y). So, it's not entirely clear what you mean when you say, 'if two numbers are numbers of the same power'. Notwithstanding, you can easily (without using brute force) find y given x and z, using the log() function in php, like so:
$z=125;
$x=5;
$y=log($z, $x);
print $z . ' is ' . $x . ' to the power of ' . $y . '.';
This should produce:
125 is 5 to the power of 3.
What would be the solution to have:
7.1 => 7
7.5 => 7
7.8 => 8
So I need to round number or floor depending on the number after the comma.
How to do that?
Thanks.
You should be able to use the constant, PHP_ROUND_HALF_DOWN, to have the round function round down when it is half way.
echo round(7.1, 0, PHP_ROUND_HALF_DOWN) . "\n";
echo round(7.5, 0, PHP_ROUND_HALF_DOWN) . "\n";
echo round(7.8, 0, PHP_ROUND_HALF_DOWN) . "\n";
Output:
7
7
8
From the manual:
Round val down to precision decimal places towards zero, when it is half way there. Making 1.5 into 1 and -1.5 into -1.
PHP Demo: https://eval.in/427706
One way to do this is to split the value at the decimal (or comma? your example is using decimals) and test the trailing number to see whether you want to use floor or ceiling.
$test = 7.6
$arrayTest = explode(".",$test);
if(isset($arrayTest[1]) && $arrayTest[1] > 5) {
//do something
} else {
//do something else
}
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
}
?>
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.
my question is: is there a good (common) algorithm to create numbers, which match well looking user understood numbers out of incomming (kind of random looking for a user) numbers.
i.e. you have an interval from
130'777.12 - 542'441.17.
But for the user you want to display something more ...say userfriendly, like:
130'000 - 550'000.
how can you do this for several dimensions?
an other example would be:
23.07 - 103.50 to 20 - 150
do you understand what i mean?
i should give some criteria as well:
the interval min and max should
include the given limits.
the "rounding" should be in a
granularity which reflects the
distance between min and max (meaning
in our second example 20 - 200
would be too coarse)
very much honor you'll earn if you know a native php function which can do this :-)
*update - 2011-02-21 *
I like the answer from #Ivan and so accepted it. Here is my solution so far:
maybe you can do it better. i am open for any proposals ;-).
/**
* formats a given float number to a well readable number for human beings
* #author helle + ivan + greg
* #param float $number
* #param boolean $min regulates wheter its the min or max of an interval
* #return integer
*/
function pretty_number($number, $min){
$orig = $number;
$digit_count = floor(log($number,10))+1; //capture count of digits in number (ignoring decimals)
switch($digit_count){
case 0: $number = 0; break;
case 1:
case 2: $number = round($number/10) * 10; break;
default: $number = round($number, (-1*($digit_count -2 )) ); break;
}
//be sure to include the interval borders
if($min == true && $number > $orig){
return pretty_number($orig - pow(10, $digit_count-2)/2, true);
}
if($min == false && $number < $orig){
return pretty_number($orig + pow(10, $digit_count-2)/2, false);
}
return $number;
}
I would use Log10 to find how "long" the number is and then round it up or down. Here's a quick and dirty example.
echo prettyFloor(23.07);//20
echo " - ";
echo prettyCeil(103.50);//110
echo prettyFloor(130777.12);//130000
echo " - ";
echo prettyCeil(542441.17);//550000
function prettyFloor($n)
{
$l = floor(log(abs($n),10))-1; // $l = how many digits we will have to nullify :)
if ($l<=0)
$l++;
if ($l>0)
$n=$n/(pow(10,$l)); //moving decimal point $l positions to the left eg(if $l=2 1234 => 12.34 )
$n=floor($n);
if ($l>0)
$n=$n*(pow(10,$l)); //moving decimal point $l positions to the right eg(if $l=2 12.3 => 1230 )
return $n;
}
function prettyCeil($n)
{
$l = floor(log(abs($n),10))-1;
if ($l<=0)
$l++;
if ($l>0)
$n=$n/(pow(10,$l));
$n=ceil($n);
if ($l>0)
$n=$n*(pow(10,$l));
return $n;
}
This example unfortunately will not convert 130 to 150. As both 130 and 150 have the same precision. Even thou for us, humans 150 looks a bit "rounder". In order to achieve such result I would recommend to use quinary system instead of decimal.
You can use php's round function which takes a parameter to specify the precision.
<?php
echo round(3.4); // 3
echo round(3.5); // 4
echo round(3.6); // 4
echo round(3.6, 0); // 4
echo round(1.95583, 2); // 1.96
echo round(1241757, -3); // 1242000
echo round(5.045, 2); // 5.05
echo round(5.055, 2); // 5.06
?>
The number_format() function handles "prettifying" numbers with arbitrary thousands/decimal characters and decimal places, but you'd have to split your ranges/strings into individual numbers, as number_formation only works on one number at a time.
The rounding portion would have to handled seperately as well.
I haven't seen ready algorithm or function for that. But it should be simple, based on string replacement (str_replace, preg_replace), number_format and round functions.
This actually is kind of a special case, that can be addressed with the following function:
function roundto($val, $toceil=false) {
$precision=2; // try 1, 2, 5, 10
$pow = floor(log($val, 10));
$mult = pow(10, $pow);
$a = $val/$mult*$precision;
if (!$toceil) $a-=0.5; else $a+=0.5;
return round($a)/$precision*$mult;
}
$v0=130777.12; $v1=542441.17;
echo number_format(roundto($v0, false), 0, '.', "'").' - '
.number_format(roundto($v1, true), 0, '.', "'").'<br/>';
$v0=23.07; $v1=103.50;
echo number_format(roundto($v0, false), 0, '.', "'").' - '
.number_format(roundto($v1, true), 0, '.', "'").'<br/>';
Outputs exactly this:
100'000 - 550'000
20 - 150
For any other case of number formatting it might be interesting to have a look at my newly published PHP class "php-beautiful-numbers", which I use in almost ever project to display run times ("98.4 µs" [= 9.8437291615846E-5]) or numbers in running text (e.g. "you booked two flights." [= 2]).
https://github.com/SirDagen/php-beautiful-numbers