views:

807

answers:

7

Based on this question: Is there a way to round numbers into a friendly format?

THE CHALLENGE - UPDATED! (removed hundreds abbreviation from spec)

The shortest code by character count that will abbreviate an integer (no decimals).

Code should include the full program.

Relevant range is from 0 - 9,223,372,036,854,775,807 (the upper limit for signed 64 bit integer).

The number of decimal places for abbreviation will be positive. You will not need to calculate the following: 920535 abbreviated -1 place (which would be something like 0.920535M).

Numbers in the tens and hundreds place (0-999) should never be abbreviated (the abbreviation for the number 57 to 1+ decimal places is 5.7dk - it is unneccessary and not friendly).

Remember to round half away from zero (23.5 gets rounded to 24). Banker's rounding is verboten.

Here are the relevant number abbreviations:

h = hundred (102)
k = thousand (103)
M = million (106)
G = billion (109)
T = trillion (1012)
P = quadrillion (1015)
E = quintillion (1018)

SAMPLE INPUTS/OUTPUTS (inputs can be passed as separate arguments):

First argument will be the integer to abbreviate. The second is the number of decimal places.

12 1                  => 12 // tens and hundreds places are never rounded
1500 2                => 1.5k
1500 0                => 2k // look, ma! I round UP at .5
0 2                   => 0
1234 0                => 1k
34567 2               => 34.57k
918395 1              => 918.4k
2134124 2             => 2.13M
47475782130 2         => 47.48G
9223372036854775807 3 => 9.223E
// ect...

Original answer from related question (JavaScript, does not follow spec):

function abbrNum(number, decPlaces) {
    // 2 decimal places => 100, 3 => 1000, etc
    decPlaces = Math.pow(10,decPlaces);

    // Enumerate number abbreviations
    var abbrev = [ "k", "m", "b", "t" ];

    // Go through the array backwards, so we do the largest first
    for (var i=abbrev.length-1; i>=0; i--) {

        // Convert array index to "1000", "1000000", etc
        var size = Math.pow(10,(i+1)*3);

        // If the number is bigger or equal do the abbreviation
        if(size <= number) {
             // Here, we multiply by decPlaces, round, and then divide by decPlaces.
             // This gives us nice rounding to a particular decimal place.
             number = Math.round(number*decPlaces/size)/decPlaces;

             // Add the letter for the abbreviation
             number += abbrev[i];

             // We are done... stop
             break;
        }
    }

    return number;
}
+6  A: 

Python 2.x, 78 chars

a=input()
i=0
while a>=1e3:a/=1e3;i+=1
print"%g"%round(a,input())+" kMGTPE"[i]

This version (75 chars) uses printf which will print extra zeros and follows the round-to-even rule.

a=input()
i=0
while a>=1e3:a/=1e3;i+=1
print"%%.%df"%input()%a+" kMGTPE"[i]
KennyTM
has anyone verified this with some test cases?
David Murdoch
+3  A: 

Javascript 114 chars

function m(n,d){p=M.pow
d=p(10,d)
i=7
while(i)(s=p(10,i--*3))<=n&&(n=M.round(n*d/s)/d+"kMGTPE"[i])
return n}

Also 114 - Using spidermonkey - Input on STDIN

[n,d]=readline().split(' '),x=n.length,p=Math.pow,d=p(10,d)
x-=x%3
print(Math.round(n*d/p(10,x))/d+" kMGTPE"[x/3])

108 - Function

function m(n,d){x=(''+n).length,p=Math.pow,d=p(10,d)
x-=x%3
return Math.round(n*d/p(10,x))/d+" kMGTPE"[x/3]}

Which also becomes 103 if you replace the (''+n) with n and promise to only pass strings :)

David Murdoch
I know _someone_ has some clever way of shortening this! Maybe with some 1.6+ features or 1.8's `[].reduce`?
David Murdoch
+4  A: 

Perl 114 111 104 chars

My first ever code-golf entry!

Arguments provided from standard input: perl fna.pl 918395 1

($n,$d)=@ARGV;
@n=$n=~/./g;
@s=' kMGTPE'=~/./g;
printf"%.".(@n>3?$d:0)."f%s",$n/(10**($#n-$#n%3)),$s[@n/3];

Output:

918.4k


De-golfed version (with explanation):

( $number, $dp ) = @ARGV;      # Read in arguments from standard input

@digits = split //, $number;   # Populate array of digits, use this to count
                               # how many digits are present

@suffix = split //, ' kMGTPE'; # Generate suffix array

$number/(10**($#n-$#n%3));     # Divide number by highest multiple of 3

$precision = @n>3 ? $dp : 0;   # Determine number of decimal points to print

sprintf "%.".$precision."f%s", # "%.2f" prints to 2 dp, "%.0f" prints integer
        $number, $suffix[@n/3];# Select appropriate suffix
Zaid
+10  A: 

J, 61 63 65 characters

((j.&(1&{)":({.%&1e3{:));{&' kMGTPE'@{.)(([:<.1e3^.{.),{:,{.)

Output:

((j.&(1&{)":({.%&1e3{:));{&' kMGTPE'@{.)(([:<.1e3^.{.),{:,{.) 1500 0
┌─┬─┐
│2│k│
└─┴─┘

((j.&(1&{)":({.%&1e3{:));{&' kMGTPE'@{.)(([:<.1e3^.{.),{:,{.) 987654321987654321 4
┌────────┬─┐
│987.6543│P│
└────────┴─┘

(The reason the output is "boxed" like that is because J doesn't support a list consisting of varying types)

Explanation (from right to left):

(([:<.1000^.{.),{:,{.)

We make a new 3-element list, using , to join ([:<.1000^.{.) (the floored <. base 1000 log ^. of the first param {.. We join it with the second param {: and then the first param {..

So after the first bit, we've transformed say 12345 2 into 1 2 12345

((j.&(1&{)":({.%&1000{:));{&' kMGTPE'@{.) uses ; to join the two halves of the expression together in a box to produce the final output.

The first half is ((j.&(1&{)":({.%&1000{:)) which divides (%) the last input number ({:) by 1000, the first number of times. Then it sets the precision ": using the second number in the input list (1&{).

The second half {&' kMGTPE'@{. - this uses the first number to select ({) the appropriate character from the 0-indexed list of abbreviations.

David
output for `987654321987654321 4` should be `987.6543P`. and out put for `1234567 2` should be `1.23M`
David Murdoch
@David: I know, I caught that after posting it. Just spent the last half hour fixing that at a cost of 2 characters. :)
David
Also, it supports up to 999,999,999,999,999,999,999 - over 100x higher than the requirements.
David
Ok, after looking at your code I would like to say that your family is worried about you
M28
@M28: I'll take that as a compliment :)
David
+3  A: 

dc - 75 chars

A7 1:U77 2:U71 3:U84 4:U80 5:U69 6:U[3+r1-r]sJ?sddZd3~d0=Jrsp-Ar^ldk/nlp;UP

Uses Z (number of digits) %3 to find the unit. Most of the code is for setting the units character array, the real code is 39 chars. The J macro adjusts when %3 equals 0, to avoid printing 0.918M in the 7th. test case. It doesn't round properly.

If you speak dc, feel free to improve it.

Carlos Gutiérrez
+3  A: 

Ruby - 79 77 75 chars

n,d=ARGV
l=n.size
printf"%.#{l>3?d:0}f%s",n.to_f/10**(l-l%3)," kMGTPE"[l/3]

Reads from command line arguments.

74 72 chars, prints output within double quotes

n,d=ARGV
l=n.size
p"%.#{l>3?d:0}f%s"%[n.to_f/10**(l-l%3)," kMGTPE"[l/3]]

66 chars, prints extra zeroes

n,d=ARGV
l=n.size
p"%.#{d}f%s"%[n.to_f/10**(l-l%3)," kMGTPE"[l/3]]

Based on this solution, and the sample code.

Utkarsh
+1  A: 

Perl 94 Chars

($_,$d)=@ARGV;$l=length;@u=' kMGTPE'=~/./g;printf"%.".($l>3?$d:0)."f$u[$l/3]",$_/10**($l-$l%3)

Usage:

perl abbreviator.pl 47475782130 2

Output:

47.48G
M42