6
\$\begingroup\$

This time, we want render the count of bytes in a human readable way to user. Let's write a program (a full one or a function or other acceptable formats) to do this.

Input

An non-negative integer, in range \$0\leq n<2^{31}\$, which means n bytes.

Output

A string, the human friendly representation of n bytes.

Convention

  • If n is less than 1000, add B after n and output; otherwise...
  • Convert to a number less than 1000, plus one of these units: KiB, MiB, GiB; round the number to 3 figures
  • For more details of conversion, check out these testcases

Testcases

0 -> 0B
1 -> 1B
42 -> 42B
999 -> 999B
1000 -> 0.98KiB
1024 -> 1.00KiB
2018 -> 1.97KiB
10086 -> 9.85KiB
100010 -> 97.7KiB
456789 -> 446KiB
20080705 -> 19.2MiB
954437177 -> 910MiB
1084587701 -> 1.01GiB
1207959551 -> 1.12GiB
2147483647 -> 2.00GiB

Rules

  • This is , so shortest bytes win
  • Standard loopholes forbidden
  • You should output exactly the same to the testcase:
    • No white space or other symbols between number and units;
    • Use KiB, not KB, kib, or kb;
    • Leading or trailing white spaces are optional
\$\endgroup\$
16
  • \$\begingroup\$ @tsh "Use KiB, not KB" - are you sure about the 1000 then? The SI units are 1kB=1000B and 1KiB=1024B. \$\endgroup\$
    – ngn
    Commented Jul 7, 2018 at 8:30
  • \$\begingroup\$ @ngn Yes. 1000 should be converted to 0.98KiB, not 1000B, or 1kB. And there are always at most 3 significant figures, not 4 (for 1000B). \$\endgroup\$
    – tsh
    Commented Jul 7, 2018 at 8:33
  • 3
    \$\begingroup\$ I think 0.98KiB is using 2 significant figures, not 3 – the leading 0 doesn't count. \$\endgroup\$ Commented Jul 7, 2018 at 8:36
  • 1
    \$\begingroup\$ @user202729 The 3 figures rule seems to apply only if \$n\ge1000\$. \$\endgroup\$ Commented Jul 7, 2018 at 11:00
  • 7
    \$\begingroup\$ I remember seeing this question in the Sandbox, though I don't know how long it was there - either it should have been there longer, or more people need to visit the Sandbox and leave comments there. \$\endgroup\$
    – Sundar R
    Commented Jul 7, 2018 at 12:44

10 Answers 10

4
\$\begingroup\$

JavaScript (ES6), 78 74 bytes

f=(n,i)=>n<999.5?i?n.toPrecision(n<1^3)+' KMG'[i]+'iB':n+'B':f(n/1024,-~i)
<input oninput=o.textContent=f(this.value)><pre id=o>

Edit: Saved 4 bytes thanks to @Arnauld.

\$\endgroup\$
2
  • \$\begingroup\$ 1023079 outputs 0.98MiB should probably be 999KiB. \$\endgroup\$
    – user15259
    Commented Jul 7, 2018 at 12:43
  • \$\begingroup\$ @YiminRong Fixed for n up to 1023487, then rolls over for 1023488. \$\endgroup\$
    – Neil
    Commented Jul 7, 2018 at 12:47
2
\$\begingroup\$

C (gcc), 108 102 bytes

Works as Yimin Rong suggests.

f(float i){char*a="GiMiKi"+6;while(i>=999.5)i/=1024,a-=2;printf("%.*f%.2sB",*a&i<100?i<10?2:1:0,i,a);}

Try it online!

Unglofed

f(float i){
    char*a="GiMiKi"+6;
    while(i>=999.5)
        i/=1024,a-=2;
    printf("%.*f%.2sB",*a&i<100?i<10?2:1:0,i,a);
}

C (gcc), 108 101 99 bytes

Works as O.O.Balance suggests.

f(float i){char*a="GiMiKi"+6;while(i>999)i/=1024,a-=2;printf("%.*f%.2sB",*a&i<100?i<10?2:1:0,i,a);}

Try it online!

Unglofed

f(float i){
    char*a="GiMiKi"+6;
    while(i>999)
        i/=1024,a-=2;
    printf("%.*f%.2sB",*a&i<100?i<10?2:1:0,i,a);
}
\$\endgroup\$
4
  • \$\begingroup\$ f(1023079) outputs 0.98MiB, output should probably be 999KiB. \$\endgroup\$
    – user15259
    Commented Jul 7, 2018 at 11:53
  • \$\begingroup\$ @YiminRong Reading the question, I tend to disagree; I think this should be treated as an extension of the ’1000’ case. \$\endgroup\$ Commented Jul 7, 2018 at 11:59
  • \$\begingroup\$ You should remove the ’include’ from the header or add it to the byte count. It works fine without it though. \$\endgroup\$ Commented Jul 7, 2018 at 12:01
  • \$\begingroup\$ f(1023488) outputs 1000KiB for the first one, output should probably be 0.98MiB. Using >=999.5 in the while loop fixes it. \$\endgroup\$
    – user15259
    Commented Jul 7, 2018 at 12:36
2
\$\begingroup\$

Python3, 90 bytes

(76 70 bytes 3-most-significant-digits version)

h=lambda v,u=0:v>999.4and h(v/1024,u+2)or"%.*f%sB"%(u and(v<10)+(v<100),v,"KiMiGi"[u-2:u])

Version whose outputs are strictly adherent to test cases and author's suggestions.

list(map(lambda v: print("{} --> {}".format(v, h(v))), (0, 1, 42, 999, 1000, 124, 2018, 10086, 100010, 456789, 20080705, 954437177, 1084587701, 1207959551, 2147483647, 1023079)))

0 --> 0B
1 --> 1B
42 --> 42B
999 --> 999B
1000 --> 0.98KiB
1024 --> 1.00KiB
2018 --> 1.97KiB
10086 --> 9.85KiB
100010 --> 97.7KiB
456789 --> 446KiB
20080705 --> 19.2MiB
954437177 --> 910MiB
1084587701 --> 1.01GiB
1207959551 --> 1.12GiB
2147483647 --> 2.00GiB
1023079 --> 999KiB
1023488 --> 0.98MiB
1043333 --> 0.99MiB
1043334 --> 1.00MiB

Try it online!

Here, a version that produces the most 3 significant digits for the test cases.

h=lambda v,u=0:v<1e3and("%.3G%sB"%(v,"KiMiGi"[u-2:u]))or h(v/1024,u+2)

The original test cases:

list(map(lambda v: print("{} --> {}".format(v, h(v))), (0, 1, 42, 999, 1000, 124, 2018, 10086, 100010, 456789, 20080705, 954437177, 1084587701, 1207959551, 2147483647)))

0 --> 0B
1 --> 1B
42 --> 42B
999 --> 999B
1000 --> 0.977KiB
124 --> 124B
2018 --> 1.97KiB
10086 --> 9.85KiB
100010 --> 97.7KiB
456789 --> 446KiB
20080705 --> 19.2MiB
954437177 --> 910MiB
1084587701 --> 1.01GiB
1207959551 --> 1.12GiB
2147483647 --> 2GiB

Try it online!

\$\endgroup\$
6
  • \$\begingroup\$ Sorry, I should have been more specific. You are outputting ’2GiB’ instead of ’2.00GiB’. \$\endgroup\$ Commented Jul 7, 2018 at 11:38
  • 2
    \$\begingroup\$ 1023488 outputs 1e+03KiB, should probably be 0.98MiB. \$\endgroup\$
    – user15259
    Commented Jul 7, 2018 at 12:47
  • \$\begingroup\$ @O.O.Balance, fixed ;). Thanks to point it out. \$\endgroup\$
    – PieCot
    Commented Jul 7, 2018 at 19:50
  • \$\begingroup\$ @YiminRong I've updated the code to be adherent to the post author's guidelines \$\endgroup\$
    – PieCot
    Commented Jul 7, 2018 at 19:52
  • \$\begingroup\$ 1023488 --> 1000KiB \$\endgroup\$ Commented Jul 7, 2018 at 21:05
1
\$\begingroup\$

APL (Dyalog Classic), 60 bytes

{'BKMGi'[i,4 0/⍨×i],⍨v⍕⍨(3-≢⍕⌊v←⍵÷i⊃p)××i←⍵+.≥1e3×p←1024*⍳4}

Try it online!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Using 1023488 outputs 1000KiB, should probably be 0.98MiB. \$\endgroup\$
    – user15259
    Commented Jul 7, 2018 at 12:42
  • \$\begingroup\$ @YiminRong the challenge says "Convert to a number less than 1000, plus one of these units: KiB, MiB, GiB; round the number to 3 figures" - that is precisely what I've done: the number is 1023488/1024 = 999.5 < 1000, the unit is KiB, when rounded the number becomes 1000, which does have 3 significant figures (the leading 1,0,0) and the rest are 0 (the trailing 0). \$\endgroup\$
    – ngn
    Commented Jul 7, 2018 at 17:20
1
\$\begingroup\$

Rust, 275 bytes

fn h(n:u32)->String{let i=[(30,"Gi"),(20,"Mi"),(10,"Ki"),(0,"")].iter().map(|x| (n as f32/2f32.powi(x.0),x.1)).filter(|y| y.0<999.5).last().unwrap();format!("{}{}B",if n<1000{format!("{}",n)}else{format!("{:.4}",format!("{:.2}",i.0)).trim_right_matches(".").to_string()},i.1)}

The basic idea is borrowed from the other answers, but formatting the string to have the proper number of significant digits, not have trailing decimal points, and not have numbers like 423.78 takes over half of the code... and it still rounds wrong (19.1 should be 19.2)

\$\endgroup\$
2
  • \$\begingroup\$ A TIO link with some test cases would be nice. I always like to run the code :D \$\endgroup\$ Commented Jul 7, 2018 at 23:05
  • 1
    \$\begingroup\$ bit.ly/2J7wseA \$\endgroup\$
    – don bright
    Commented Jul 7, 2018 at 23:51
1
\$\begingroup\$

Java 8, 130 112 108 bytes

n->n<1e3?n+"B":"".format("%.3G"+(n<1e9?n<1e6?"K":"M":"G")+"iB",n<1e9?n<1e6?n/1024.:n/1048576.:n/1073741824.)

Uses 3 significant figures, as per the original challenge. Try it online here.

Thanks to Jakob for golfing 4 bytes.

Ungolfed:

n -> // lambda 
n < 1e3 // special case: number of bytes is < 1000
    ? n + "B" // output the number of bytes
    : "".format("%.3G" // otherwise output 3 significant digits
    + (n < 1e9 // if it's less than 1 billion bytes
        ? n < 1e6 // and it's less than a million bytes
            ? "K" // output kibibytes
            : "M" // else output mebibytes
        : "G") // else output gibibytes
        + "iB", // common suffix
        n < 1e9 // if it's less than 1 billion bytes
            ? n < 1e6 // if it's less than 1 million bytes
                ? n/1024.     // convert to kibibytes
                : n/1048576.  // else convert to mebibytes
            : n/1073741824. ) // else convert to gibibytes
\$\endgroup\$
2
  • \$\begingroup\$ You can replace String.format with "".format. \$\endgroup\$
    – Jakob
    Commented Jul 7, 2018 at 22:27
  • \$\begingroup\$ @Jakob Thanks, indeed I can. I always forget that you can invoke static methods like that. \$\endgroup\$ Commented Jul 7, 2018 at 23:06
1
\$\begingroup\$

Stax, 34 bytes

ì▌╚ƒ░¿n↕─.êN┴▀m▌@àΔ*¡I⌠'}¿Äüz♥∞└6]

Run and debug it

Writing this, I ran into a bug in stax's log10 operation. Log10 of 1000 yields 2.9999...., which is particularly unhelpful for this challenge. I ended up using string length instead. Unpacked, ungolfed, and commented, it looks like this.

Vk<             is input less than 1000?
{               start block one
  yp'BP         print input followed by "B"
}               end block one
{               start block two
  x             push input as number
  y%v3/X        push (len(input) - 1) / 3, and store in the X register
  A*|2          multiply by 10, then get calculate two-power
  :_2j          floating point division, rounded to 2 places as a string
  p             print without trailing newline
  "GKM"[email protected]+P  use x register to index into "GKM" string, then concat "ib" and print
}               end block two
?               if-else based on initial condition

Run this one

\$\endgroup\$
0
\$\begingroup\$

Python 3, 87 81 80 bytes

def f(n):
 for u in'','Ki','Mi','Gi':
  if n<1e3:return"%.3G%sB"%(n,u)
  n/=1024

Try it online!

This does not work, I will need to think about how to fix it.

-6 bytes from Jo King

-1 bytes from Stewie Griffin

\$\endgroup\$
5
  • \$\begingroup\$ @JoKing Oh. I knew I could do in[], but didn't think I could in'' \$\endgroup\$ Commented Jul 7, 2018 at 10:28
  • \$\begingroup\$ Note that it doesn't match the format exactly, leaving out zeros after the decimal point. \$\endgroup\$ Commented Jul 7, 2018 at 10:29
  • \$\begingroup\$ @O.O.Balance Does it have to match exactly? 0B, 1B, 42B are not 3 sig figs. Also to get pedantic it should be 910.MiB. \$\endgroup\$ Commented Jul 7, 2018 at 10:36
  • \$\begingroup\$ Well, it says: "You should output exactly the same to the testcase" \$\endgroup\$ Commented Jul 7, 2018 at 10:40
  • 1
    \$\begingroup\$ 1023488 outputs 1E+03KiB, should probably be 0.98MiB. \$\endgroup\$
    – user15259
    Commented Jul 7, 2018 at 12:49
0
\$\begingroup\$

C (gcc), 135 bytes

e;double atof(),x;main(i,v)int**v;{for(x=atof(*++v);x>=999.5;e++)x/=1024;printf(e?"%.*f%ciB":"%.*fB",e?2-(int)log10(x):0,x,".KMG"[e]);}

Try it online!

Quite change from Yimin Rong's solution

\$\endgroup\$
0
\$\begingroup\$

C, 154 165 191 189 bytes (full program)

Big thanks to O.O.Balance and l4m2 for suggestions and providing this TIO and this TIO!

double atof(),x;main(i,v)char**v;{x=atof(*++v);if(x<1e3)printf("%sB",*v);else{for(;x>=999.5;++i)x/=1024;printf("%.*f%ciB",2-(int)log10(x),x,"KMG"[i-3]);}}

Save as doit.c, compile using gcc -o doit doit.c -lm, and run:

> ./doit 1000
0.98KiB

Test cases:

> for i in 0 1 42 999 1000 1024 2018 10086 100010 456789 1023079 1023488 20080705 954437177 1084587701 1207959551 2147483647 ; do ./doit $i ; echo; done
0B
1B
42B
999B
0.98KiB
1.00KiB
1.97KiB
9.85KiB
97.7KiB
446KiB
999KiB
0.98MiB
19.2MiB
910MiB
1.01GiB
1.12GiB
2.00GiB
\$\endgroup\$
7
  • \$\begingroup\$ You can replace the while loop with for and get rid of braces; use K&R style arguments for main, get rid of puts("B"); by duplicating it in both branches. Also, it works fine for me without the second function declaration (remove the typedef as well). => 165 bytes: tio.run/##PctBC4IwAAXgvxIDYZvTNquDLBO6dIh@QXRYa7PBdKEmA/… \$\endgroup\$ Commented Jul 7, 2018 at 12:48
  • \$\begingroup\$ @O.O.Balance -- Wow! I can never compete with the pros at golfing C! \$\endgroup\$
    – user15259
    Commented Jul 7, 2018 at 12:56
  • \$\begingroup\$ float atof();main(i,v)char**v;{double x=atof(*++v);if(x<1e3)printf("%sB",*v);else{for(;x>=999.5;++i)x/=1024;printf("%.*f%ciB",2-(int)log10(x),x,"???KMG"[i]);}}? \$\endgroup\$
    – l4m2
    Commented Jul 8, 2018 at 11:24
  • \$\begingroup\$ @l4m2 This gives output 1000B for input 1000. See here: tio.run/##PYvNCoIwAIBfRQbB/… \$\endgroup\$ Commented Jul 8, 2018 at 19:10
  • \$\begingroup\$ @O.O.Balance Still use double fine \$\endgroup\$
    – l4m2
    Commented Jul 9, 2018 at 0:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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