15
\$\begingroup\$

Challenge and origin

On Stack Overflow a popular question is: How to convert byte size into human readable format in java? The most up voted answer has a quite nice method for doing this, but this is codegolf and we can do better, can't we?

Your challenge is to write a method or program that coverts the given number of bytes to the correct human readable format and prints the result to the standard out of your language.*

*See the rules for more clarification!

Input

The input will always be a positive number of bytes with a maximum of (2^31)-1.

Output

You may choose if you prefer the International System of Units or the binary notation as output (the SI notation probably saves you some bytes).

SI:      B, kB,  MB,  GB  
Binary:  B, KiB, MiB, GiB

Note: Higher units than GB or GiB are not posible due to the restricted input range.

Example output

International System of Units:

Input       Output
0           0.0     B
999         999.0   B
1000        1.0     kB
1023        1.0     kB
1024        1.0     kB
1601        1.6     kB
160581      160.6   kB
4066888     4.1     MB
634000000   634.0   MB
2147483647  2.1     GB

Binary:

Input       Output
0           0.0     B
999         999.0   B
1000        1000.0  B
1023        1023.0  B
1024        1.0     KiB
1601        1.6     KiB
160581      156.8   KiB
4066888     3.9     MiB
634000000   604.6   MiB
2147483647  2.0     GiB

Rules

  • Build-in functions for byte formatting are not allowed!
  • The output should always be in the same notation standard, you may not mix SI or binary;
  • The output should always be in the largest unit possible where the resulting number is still higher or equal to one;
  • The output should always have one decimal number, but you may choose to print an integer number when the resulting output is in bytes (B);
  • You may choose if you would like to add a space, tab or nothing between the number and the unit;
  • Input is received via STDIN or function parameters;
  • Output is printed to the console or returned as string (or similar character container);
  • This is code golf, so the shortest answer wins. Have fun!

Edit: Even more clarification

Some numbers have interesting rounding behaviors like the number 999950. Most code implementations would return 1000.0 kB instead of 1.0 MB. Why? Because 999950/1000 evaluates to 999.950 which is effectively rounded to 1000.0 when using String.format in Java (in most other languages too). Hench some extra checks are needed to handle cases like this.

For this challenge both styles, 1000.0 kB and 1.0 MB are accepted, although the last style is preferred.

Pseudo code / java test code:


public static String bytesToSI(long bytes){
      if (bytes < 1000){
          return bytes + ".0 B";
      }
      //Without this rounding check:
      //999950    would be 1000.0 kB instead of 1.0 MB
      //999950000 would be 1000.0 MB instead of 1.0 GB
      int p = (int) Math.ceil(Math.log(bytes) / Math.log(1000));
      if(bytes/Math.pow(1000, p) < 0.99995){
          p--;
      }
      //Format
      return String.format("%.1f %sB", bytes/Math.pow(1000, p), "kMGTPE".charAt(p-1));
}

\$\endgroup\$
  • 1
    \$\begingroup\$ Technically, SI kilobytes should use kB (note the lowercase k) \$\endgroup\$ – SuperJedi224 Jun 20 '15 at 11:21
  • \$\begingroup\$ Good point, fixed! \$\endgroup\$ – Rolf ツ Jun 20 '15 at 11:21
  • 1
    \$\begingroup\$ I don't want to limit to much, so I would say the spacing may be inconsistent. But with this rule: The difference in space and tab characters for different valid inputs may not exceed 10. (To keep it all a bit "human readable") \$\endgroup\$ – Rolf ツ Jun 20 '15 at 14:05
  • 2
    \$\begingroup\$ What's the expected output for 999999 and 1000000? 160581 exhibits rounding, so should it be 1000.0kB and 1.0MB? \$\endgroup\$ – Sp3000 Jun 20 '15 at 15:41
  • 3
    \$\begingroup\$ @Sp3000 That's a good question, the nicest solution would be for 999999 to display 1.0 MB. But for this challenge I would say 1000.0 KB and similar rounding cases are fine too. \$\endgroup\$ – Rolf ツ Jun 20 '15 at 17:32

18 Answers 18

10
\$\begingroup\$

TI-BASIC, 44

Would be the right tool for the job if TI-BASIC had halfway decent string manipulation (I had to resort to overwriting the exponent of the number, displayed in engineering notation, with the unit). As it is it rounds and outputs correctly, but it's not even close to winning entries. Maybe a different calculator language could win this one?

Fix 1
Eng
ClrHome
Disp Ans
Output(1,15,sub(" kMG",1+iPart(log(Ans+.5)/3),1)+"B

Input in the form [number]:[program name] on the homescreen.

Given test cases:

Input       Output (leading spaces intentional; screen clear before each output)
0                      0.0 B
999                  999.0 B
1000                   1.0kB
1023                   1.0kB
1024                   1.0kB
1601                   1.6kB
160581               160.6kB
4066888                4.1MB
634000000            634.0MB
2147483647             2.1GB
\$\endgroup\$
  • \$\begingroup\$ I had absolutely no idea that TI-BASIC was so versatile haha \$\endgroup\$ – Beta Decay Jun 20 '15 at 20:12
  • 1
    \$\begingroup\$ TI-BASIC isn't versatile, but there are often strange workarounds for a few of its shortcomings. \$\endgroup\$ – lirtosiast Jun 21 '15 at 5:59
6
\$\begingroup\$

CJam, 35 27 bytes

ri{_e-3_i}g;1mOo]," kMG"='B

Thanks Dennis for removing 8 bytes.

This doesn't print .0 in the online interpreter. But as Dennis has pointed out, it works fine in the Java interpreter.

Explanation

ri         e# Read the input as an integer.
{          e# Do:
    _e-3   e#   Make a copy and divide by 1000.
           e#   This will generate one more item in the stack for each iteration.
    _i     e#   Make a copy and truncate to integer.
}g         e# until the integer part is 0.
;          e# Discard the final value with integer part 0.
1mOo       e# Output the number before it with the correct format.
],         e# Count the number of iterations - 1.
" kMG"=    e# Select a character according to the number of iterations.
'B         e# Output B.
\$\endgroup\$
  • \$\begingroup\$ ri{_e-3XmO_i}g;o]," kMG"='B (27 bytes) \$\endgroup\$ – Dennis Jun 20 '15 at 16:56
  • \$\begingroup\$ @Dennis Thanks for the 1mO. But this code doesn't work for 1149999... \$\endgroup\$ – jimmy23013 Jun 20 '15 at 17:16
  • \$\begingroup\$ ri{_e-3_i}g;1mOo]," kMG"='B should. \$\endgroup\$ – Dennis Jun 20 '15 at 17:18
  • \$\begingroup\$ Scratch that, that has other bugs. \$\endgroup\$ – Dennis Jun 20 '15 at 17:19
  • \$\begingroup\$ 999999 becomes 1000kB. Reading the question again, I'm not sure if 1000kB would actually be wrong. \$\endgroup\$ – Dennis Jun 20 '15 at 17:28
5
\$\begingroup\$

Pyth, 29 27 bytes

p@" kMG"Js.lQK^T3.RcQ^KJ1\B

Demonstration. Test Harness.

Explanation:

p@" kMG"Js.lQK^T3.RcQ^KJ1\B
                                 Implicit: Q = eval(input())
p                                print, in the order 2nd arg then 1st arg:
             K^T3                K = 10^3 = 1000
          .lQK                   log of Q base K
         s                       Floored
        J                        Store to J
 @" kMG"J                        The Jth character of ' kMG'
                     ^KJ         K^J
                   cQ            Q/K^J (Floating point division)
                 .R     1        Round to 1 decimal place.
                         \B      Print a trailing 'B'.
\$\endgroup\$
3
\$\begingroup\$

CJam, 28

r_dA@,(3/:X3*#/1mO" kMG"X='B

Try it online

Note: it doesn't show ".0" with the online interpreter, but does so with the official java interpreter.

Explanation:

r_          read and duplicate
dA          convert to double and push 10
@           bring the initial string to the top
,(          get the length and decrement
3/          divide by 3 (for thousands)
:X3*        store in X and multiply by 3 again
#           raise 10 to that power
/           divide the original number by it
1mO         round to 1 decimal
" kMG"X=    convert X from 0/1/2/3 to space/k/M/G
'B          add a 'B'
\$\endgroup\$
  • \$\begingroup\$ What is the backtick for? \$\endgroup\$ – Dennis Jun 20 '15 at 16:50
  • \$\begingroup\$ @Dennis showing .0 in the online interpreter \$\endgroup\$ – aditsu Jun 20 '15 at 16:51
  • \$\begingroup\$ It works fine in the Java interpreter without the backtick, so I don't think you need it. \$\endgroup\$ – Dennis Jun 20 '15 at 16:52
3
\$\begingroup\$

Python 2 - 76 bytes

Uses the Internation System of Units, simply because it's easier to do in your head ;)

n=input();m=0;f=1e3
while n>=f:n/=f;m+=2
print"%.1f%s"%(n,'B kBMBGB'[m:m+2])
\$\endgroup\$
  • \$\begingroup\$ it seems not ok for me, it doesn't respect the asked formatting, for exemple if I submit "2147483647" I obtain "2.000000GB" - The question asks for one decimal, and maybe a space. \$\endgroup\$ – dieter Jun 20 '15 at 12:47
  • 1
    \$\begingroup\$ Also, this is 79 bytes according to this. This is 75 bytes. I don't believe it was specified that there needs to be a space between the number and the unit. \$\endgroup\$ – Kade Jun 20 '15 at 16:30
  • \$\begingroup\$ you can save one byte with f=1e3 \$\endgroup\$ – mbomb007 Jun 22 '15 at 21:40
  • \$\begingroup\$ @mbomb007 Actually it saved 2 bytes because 1e3 is a float \$\endgroup\$ – Beta Decay Jun 23 '15 at 5:58
  • \$\begingroup\$ I knew it was a float. I guess I just can't count... \$\endgroup\$ – mbomb007 Jun 23 '15 at 14:46
2
\$\begingroup\$

POWERSHELL,190

$x=Read-Host
function f($a,$b){"$x`t"+[math]::Round($x/$a,1).ToString("F1")+"`t$b"}
if(1KB-gt$x){f 1 "B"}elseif(1MB-gt$x){f 1KB KiB}
elseif(1GB-gt$x){f 1MB MiB}elseif(1TB-gt$x){f 1GB GiB}

usage

PS C:\> .\makehum.ps1
1601
1601    1.6     KiB
PS C:\> .\makehum.ps1
4066888
4066888 3.9     MiB
PS C:\> .\makehum.ps1
160581
160581  156.8   KiB
PS C:\> .\makehum.ps1
634000000
634000000       604.6   MiB
PS C:\> .\makehum.ps1
2147483647
2147483647      2.0     GiB
PS C:\>
\$\endgroup\$
2
\$\begingroup\$

Haskell, 119

I sadly couln't find a shorter way in Haskell to ensure 1 decimal place in floats, but I'm posting for posterity.

import Text.Printf
a#n|p>=1=(a+1)#p|1<2=(a,n)where p=n/1000
m n=let(a,b)=0#n in printf"%.1f"b++["B","kB","MB","GB"]!!a

Usage:

> m 160581
"160.6kB"

Moderately less golfed version:

import Text.Printf

countThousands :: Int -> Float -> (Int, Float)
countThousands count num
 |nextNum >= 1 = countThousands (count+1) nextNum
 |otherwise    = (count,num)
 where nextNum = num/1000

printHuman :: Float -> String
printHuman n = let (a,b) = countThousands 0 n in 
  (printf "%.1f" b) ++ (["B","kB","MB","GB"]!!a)
\$\endgroup\$
2
\$\begingroup\$

Java, 106 bytes

This one's a method that takes a number and returns a string.

String f(int n){int k=0;for(;n>1e3;k++)n/=1e3;return(int)(10*n)/10.0+new String[]{"","k","M","G"}[k]+"B";}
\$\endgroup\$
  • \$\begingroup\$ You are allowed to program a function returning a string instead of a complete program, it may save you some bytes ;) \$\endgroup\$ – Rolf ツ Jun 20 '15 at 11:41
  • \$\begingroup\$ Three things: If you're converting a to a double anyway (I don't know if it's necessary), you can use 1e3 for 1000; you can convert that while() to a for() and use the free semicolons; and I don't know if this works because it seems to display all the decimal digits, not just one past the decimal place. \$\endgroup\$ – lirtosiast Jun 20 '15 at 12:46
  • \$\begingroup\$ @ThomasKwa: Last I checked, the question didn't seem to explicitly specify that. But I guess it does now. \$\endgroup\$ – SuperJedi224 Jun 20 '15 at 20:15
1
\$\begingroup\$

Python 2, 127 bytes

Using the ISU. The snippet declares a function 'C' that takes the number to be converted as argument.

C=lambda v:min(['%.1f %sB'%(x,u)for x,u in[(v/1000.0**i,'bkMG'[i])for i in range(4)]if x>=1]).replace('.0 b',' ')if v else'0 B'

Some test code:

    print 'Input\tOutput'
for v in [0,999,1000,1023,1023,1601,160581,4066888,634000000,2147483647]:
 print v,C(v)
\$\endgroup\$
  • \$\begingroup\$ You can use 1e3 instead of 1000.0 \$\endgroup\$ – mbomb007 Jun 22 '15 at 21:42
1
\$\begingroup\$

JavaScript (ES6), 71

Using SI units - A function returning the requested string.

f=(a,b=3)=>+(r=eval('a/1e'+b*3).toFixed(1))[0]?r+' kMG'[b]+'B':f(a,b-1)

This shorter one follows the rules, particularly 3 and 4

  • The output should always be in the largest unit possible where the resulting number is still higher or equal to one then 995 => 1.0kB
  • The output should always have one decimal number, but you may choose to print an integer number when the resulting output is in bytes (B) I choose not, so 10 => 10.0 B

Alas, this way, the results do not match the examples.

To match the examples, here is a longer one, special cased for small numbers (82 bytes)

f=(a,b=3)=>a<1e3?a+'B':+(r=eval('a/1e'+b--*3).toFixed(1))[0]?r+'kMG'[b]+'B':f(a,b)

Run the snippet to test (being EcmaScript 6, Firefox only)

f=(a,b=3)=>+(r=eval('a/1e'+b*3).toFixed(1))[0]?r+' kMG'[b]+'B':f(a,b-1)
Value <input id=I><button onclick="O.innerHTML+=f(+I.value)+'\n'">-></button><pre id=O></pre>

\$\endgroup\$
1
\$\begingroup\$

Python, 61 bytes

f=lambda n,i=0:"%.1f%cB"%(n," kMG"[i])*(n<1e3)or f(n/1e3,i+1)

Call like f(999). Note that 1e3 is a float, so this works with both Python 2 and Python 3.

\$\endgroup\$
1
\$\begingroup\$

PHP4.1, 63 62 bytes

Not the best golfing, but surely is quite short.

<?for($S=kMG;$B>1e3;$I++)$B/=1e3;printf("%.1f{$S[$I-1]}B",$B);

To use it, access over POST/GET or set a value in the SESSION, on the key B.

Leave the key I unset!

\$\endgroup\$
1
\$\begingroup\$

SpecBAS - 100 bytes

Using the ISU convention.

I realised that having a variable set to 1e3 (which needs a LET statement to assign it), and then using that variable in the working out, actually used more characters than just hardcoding the 1e3 where it was needed.

1 INPUT n: LET i=1
2 DO WHILE n>1e3: LET n=n/1e3: INC i: LOOP 
3 PRINT USING$("&.*0#",n);" kMG"(i);"B"
\$\endgroup\$
1
\$\begingroup\$

Ruby, 128 bytes

c=->i{p i.to_s+'B'if i<1e3;p (i/1e3).to_s+'kB'if i>=1e3&&i<1e6;p (i/1e6).to_s+'MB'if i>=1e6&&i<1e9;p (i/1e9).to_s+'GB'if i>=1e9}

I did it the long way, this is pretty bad.

Output

c[0] # => "0B"
c[999] # => "999B"
c[1000] # => "1.0kB" 
c[1023] # => "1.023kB"
c[1024] # => "1.024kB"
c[1601] # => "1.601kB"
c[160581] # => "160.581kB"
c[4066888] # => "4.066888MB"
c[634000000] # => "634.0MB"
c[2147483647] # => "2.147483647GB"

Edit

Added TB for an extra 39 bytes

c=->i{p i.to_s+'B'if i<1e3;p (i/1e3).to_s+'kB'if i>=1e3&&i<1e6;p (i/1e6).to_s+'MB'if i>=1e6&&i<1e9;p (i/1e9).to_s+'GB'if i>=1e9&&i<1e12;p (i/1e12).to_s+'TB'if i>=1e12}

Output:

c[1000000000000] # => "1.0TB"
\$\endgroup\$
1
\$\begingroup\$

Sed -r, 218+1

I'm using SI units; I think that choosing binary units would be a courageous policy. ;-)

s/(.)((...)+)$/\1z\2/;h;s/[^z]*z?//;s/.../k/g;s/kk/M/;s/Mk/G/;x;s/(z.)[5-9].*/\1c/;s/(z.c?).*/\1/;:;s/9c/c0/;s/zc/cz/;t;s/(^|0)c/1/;s/1c/2/;s/2c/3/;s/3c/4/;s/4c/5/;s/5c/6/;s/6c/7/;s/7c/8/;s/8c/9/;G;s/\n//;s/$/B/;y/z/./

Reformatted:

#!/bin/sed -rf

# Place decimal point (use z as shorthand for \.)
s/(.)((...)+)$/\1z\2/
h

# count thousands into hold space
s/[^z]*z?//
s/.../k/g
s/kk/M/;s/Mk/G/
x

# truncate to 1 decimal place
s/(z.)[5-9].*/\1c/
s/(z.c?).*/\1/

# propagate carry
:
s/9c/c0/
s/zc/cz/
t
s/(^|0)c/1/
s/1c/2/
s/2c/3/
s/3c/4/
s/4c/5/
s/5c/6/
s/6c/7/
s/7c/8/
s/8c/9/

# Append units
G;s/\n//
s/$/B/
y/z/./

Output

1 => 1B
9 => 9B
99 => 99B
999 => 999B
1000 => 1.0kB
9999 => 10.0kB
99949 => 99.9kB
99950 => 100.0kB
99999 => 100.0kB
999999 => 1000.0kB
9999999 => 10.0MB
9999999999 => 10.0GB
1000 => 1.0kB
10000 => 10.0kB
10005 => 10.0kB
10440 => 10.4kB
10450 => 10.5kB
10950 => 11.0kB

Variations

The rules seem to imply round-to-nearest, but for human display, I believe rounding down is an acceptable alternative, and saves 123 bytes (better than 50%):

s/(.)((...)+)$/\1.\2/;h;s/[^\.]*\.?//;s/.../k/g;s/kk/M/;s/Mk/G/;x;s/(\..).*/\1/;G;s/\n//;s/$/B/

The natural extension to larger units (still rounding down, 130+1 bytes):

s/(.)((...)+)$/\1.\2/;h;s/[^\.]*\.?//;s/.../k/g;s/kk/M/g;s/Mk/G/;s/MM/T/g;s/TT/Y/;s/TM/E/;s/TG/Z/;x;s/(\..).*/\1/;G;s/\n//;s/$/B/

Variation output:

1 => 1B
9 => 9B
99 => 99B
999 => 999B
1000 => 1.0kB
9999 => 9.9kB
99949 => 99.9kB
99950 => 99.9kB
99999 => 99.9kB
999999 => 999.9kB
9999999 => 9.9MB
9999999999 => 9.9GB
1000 => 1.0kB
10000 => 10.0kB
10005 => 10.0kB
10440 => 10.4kB
10450 => 10.4kB
10950 => 10.9kB
1000000000 => 1.0GB
1000000000000 => 1.0TB
1000000000000000 => 1.0MGB
1000000000000000000 => 1.0EB
1000000000000000000000 => 1.0ZB
1000000000000000000000000 => 1.0YB
999999999999999999999999999 => 999.9YB
\$\endgroup\$
  • \$\begingroup\$ Great job! I like that you thought about all the different options! \$\endgroup\$ – Rolf ツ Jun 25 '15 at 9:31
1
\$\begingroup\$

C, 77 75

f(float l){char*u=" kMG";while((l/=1e3)>=1)++u;printf("%.1f%cB",l*1e3,*u);}

This uses SI units and takes the 1000.0kB option for rounding.

Expanded code:

f(float l)
{
    char *u = " kMG";
    while ((l/=1000) >= 1)
        ++u;
    printf("%.1f%cB", l*1000, *u);
}

Output

9 => 9.0 B
9999 => 10.0kB
1023 => 1.0kB
1024 => 1.0kB
999990 => 1000.0kB
1048575 => 1.0MB
1048576 => 1.0MB
2147483647 => 2.1GB

Variants

To get binary units, change 1000 to 1024, and add i to the format string if there's a multiplier. To avoid 4-digit rounding, compare >=.95 instead of >=1. To accept larger units, extend the u string. Combining all these options, we get:

f(float l)
{
    char*u=" kMGTPEZY";
    while((l/=1024)>=.95)++u;
    printf(*u-' '?"%.1f%ciB":"%.0fB",l*1024,*u);
}

Variant output

9 => 9B
9999 => 9.8kiB
1023 => 1.0kiB
1024 => 1.0kiB
999990 => 1.0MiB
1048575 => 1.0MiB
1048576 => 1.0MiB
2147483647 => 2.0GiB
1000000000 => 953.7MiB
1000000000000 => 931.3GiB
1000000000000000 => 909.5TiB
1000000000000000000 => 888.2PiB
1000000000000000000000 => 867.4EiB
1000000000000000000000000 => 847.0ZiB
999999999999999999999999999 => 827.2YiB
1176043059457204080886151645 => 972.8YiB

Test program

Pass any number of inputs as command-line arguments:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    while (*++argv) {
        printf("%s => ", *argv);
        f(strtod(*argv, 0));
        puts("");
    }
    return 0;
}
\$\endgroup\$
  • \$\begingroup\$ Nice one ;) Well executed! \$\endgroup\$ – Rolf ツ Jun 25 '15 at 15:46
0
\$\begingroup\$

Ruby, 91 bytes

n=gets.to_i;i=0;while n>1023;n/=1024.0;i+=1;end;puts "#{n.round 1} #{%w[B KiB MiB GiB][i]}"

I could probably do a bit better if I tried harder but here's what I've got so far.

\$\endgroup\$
  • \$\begingroup\$ Use 1024. instead of 1024.0. \$\endgroup\$ – mbomb007 Jun 22 '15 at 21:45
0
\$\begingroup\$

Javascript ES5, 69 bytes

This uses a different way to reach the end goal that @edc65's answer.
Actually, this is pretty close to my PHP answer.

for(i=+prompt(z=0);i>1e3;z++)i/=1e3;alert(i.toFixed(1)+' kMG'[z]+'B')

Simply run the stack snippet or paste this on your console.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.