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;

}

Comments

Maybe add some more examples? People seem to have trouble seeing what this is about

Written by Pekka

i think so too. have researched some days and nothing found.

Written by helle

Please explain your question more clearly: why do you want to round 23.07–103.50 to 20–150 and not to 20–105 or 20–110 or 10–110 or 0–120 or 0–150 etc.? What exactly is your criterion for numbers being "pretty"? Once you're clear about what you want, the solution should present itself.

Written by ShreevatsaR

@ShreevastaR well, i don't really know that. It should just be globally userfriendly ;-) ... too little steps would be not so pretty for me (20 - 105).

Written by helle

Accepted Answer

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($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($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.

Written by Ivan
This page was build to provide you fast access to the question and the direct accepted answer.
The content is written by members of the stackoverflow.com community.
It is licensed under cc-wiki