7
\$\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\$
  • \$\begingroup\$ @tsh "Use KiB, not KB" - are you sure about the 1000 then? The SI units are 1kB=1000B and 1KiB=1024B. \$\endgroup\$ – ngn Jul 7 '18 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 Jul 7 '18 at 8:33
  • 3
    \$\begingroup\$ I think 0.98KiB is using 2 significant figures, not 3 – the leading 0 doesn't count. \$\endgroup\$ – O.O.Balance Jul 7 '18 at 8:36
  • 1
    \$\begingroup\$ @user202729 The 3 figures rule seems to apply only if \$n\ge1000\$. \$\endgroup\$ – Erik the Outgolfer Jul 7 '18 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 Jul 7 '18 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\$
  • \$\begingroup\$ 1023079 outputs 0.98MiB should probably be 999KiB. \$\endgroup\$ – Yimin Rong Jul 7 '18 at 12:43
  • \$\begingroup\$ @YiminRong Fixed for n up to 1023487, then rolls over for 1023488. \$\endgroup\$ – Neil Jul 7 '18 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\$
  • \$\begingroup\$ f(1023079) outputs 0.98MiB, output should probably be 999KiB. \$\endgroup\$ – Yimin Rong Jul 7 '18 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\$ – O.O.Balance Jul 7 '18 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\$ – O.O.Balance Jul 7 '18 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\$ – Yimin Rong Jul 7 '18 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\$
  • \$\begingroup\$ Sorry, I should have been more specific. You are outputting ’2GiB’ instead of ’2.00GiB’. \$\endgroup\$ – O.O.Balance Jul 7 '18 at 11:38
  • 2
    \$\begingroup\$ 1023488 outputs 1e+03KiB, should probably be 0.98MiB. \$\endgroup\$ – Yimin Rong Jul 7 '18 at 12:47
  • \$\begingroup\$ @O.O.Balance, fixed ;). Thanks to point it out. \$\endgroup\$ – PieCot Jul 7 '18 at 19:50
  • \$\begingroup\$ @YiminRong I've updated the code to be adherent to the post author's guidelines \$\endgroup\$ – PieCot Jul 7 '18 at 19:52
  • \$\begingroup\$ 1023488 --> 1000KiB \$\endgroup\$ – O.O.Balance Jul 7 '18 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\$
  • 1
    \$\begingroup\$ Using 1023488 outputs 1000KiB, should probably be 0.98MiB. \$\endgroup\$ – Yimin Rong Jul 7 '18 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 Jul 7 '18 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\$
  • \$\begingroup\$ A TIO link with some test cases would be nice. I always like to run the code :D \$\endgroup\$ – O.O.Balance Jul 7 '18 at 23:05
  • 1
    \$\begingroup\$ bit.ly/2J7wseA \$\endgroup\$ – don bright Jul 7 '18 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\$
  • \$\begingroup\$ You can replace String.format with "".format. \$\endgroup\$ – Jakob Jul 7 '18 at 22:27
  • \$\begingroup\$ @Jakob Thanks, indeed I can. I always forget that you can invoke static methods like that. \$\endgroup\$ – O.O.Balance Jul 7 '18 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"x@.iB+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\$
  • \$\begingroup\$ @JoKing Oh. I knew I could do in[], but didn't think I could in'' \$\endgroup\$ – WretchedLout Jul 7 '18 at 10:28
  • \$\begingroup\$ Note that it doesn't match the format exactly, leaving out zeros after the decimal point. \$\endgroup\$ – O.O.Balance Jul 7 '18 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\$ – WretchedLout Jul 7 '18 at 10:36
  • \$\begingroup\$ Well, it says: "You should output exactly the same to the testcase" \$\endgroup\$ – O.O.Balance Jul 7 '18 at 10:40
  • 1
    \$\begingroup\$ 1023488 outputs 1E+03KiB, should probably be 0.98MiB. \$\endgroup\$ – Yimin Rong Jul 7 '18 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\$
  • \$\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\$ – O.O.Balance Jul 7 '18 at 12:48
  • \$\begingroup\$ @O.O.Balance -- Wow! I can never compete with the pros at golfing C! \$\endgroup\$ – Yimin Rong Jul 7 '18 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 Jul 8 '18 at 11:24
  • \$\begingroup\$ @l4m2 This gives output 1000B for input 1000. See here: tio.run/##PYvNCoIwAIBfRQbB/… \$\endgroup\$ – O.O.Balance Jul 8 '18 at 19:10
  • \$\begingroup\$ @O.O.Balance Still use double fine \$\endgroup\$ – l4m2 Jul 9 '18 at 0:25

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.