How many bytes is it

6

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

tsh

Posted 2018-07-07T07:21:22.950

Reputation: 13 072

@tsh "Use KiB, not KB" - are you sure about the 1000 then? The SI units are 1kB=1000B and 1KiB=1024B. – ngn – 2018-07-07T08:30:48.293

@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). – tsh – 2018-07-07T08:33:10.187

3I think 0.98KiB is using 2 significant figures, not 3 – the leading 0 doesn't count. – O.O.Balance – 2018-07-07T08:36:41.190

@O.O.Balance Ah, you are right. I would change the description some how (testcases would be unchanged); So 3 <del>significant</del> figures now. – tsh – 2018-07-07T08:39:59.390

Why not leave it significant figures and change the one testcase? Significant figures is a standard way of formatting numbers (and I already have a working program :P). – O.O.Balance – 2018-07-07T08:49:00.220

@user202729 Because there isn't 0.08bits. – tsh – 2018-07-07T10:02:49.797

1@user202729 The 3 figures rule seems to apply only if $n\ge1000$. – Erik the Outgolfer – 2018-07-07T11:00:04.787

How about 1023079, should the output be 999KiB or 0.98MiB? – None – 2018-07-07T12:07:30.867

(How exactly should the numbers be rounded? Half-up?) – user202729 – 2018-07-07T12:28:17.060

Besides 1023079, another interesting test case is 1023488. – None – 2018-07-07T12:39:23.850

7I 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. – sundar - Reinstate Monica – 2018-07-07T12:44:42.383

So, the rule of the 3 digits does not apply for n < 1000, does it? – PieCot – 2018-07-07T15:12:47.833

@user202729 "3 figures" rule only apply to n >= 1000. – tsh – 2018-07-07T15:55:33.060

@PieCot "3 figures" rule only apply to n >= 1000. – tsh – 2018-07-07T15:55:55.207

@YiminRong I didn't even noticed such a testcase. Since it's too late to change anything. I would say both are OK. – tsh – 2018-07-07T15:57:16.940

@sundar Obviously the latter. – user202729 – 2018-07-07T15:59:54.137

Answers

4

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.

Neil

Posted 2018-07-07T07:21:22.950

Reputation: 95 035

1023079 outputs 0.98MiB should probably be 999KiB. – None – 2018-07-07T12:43:52.877

@YiminRong Fixed for n up to 1023487, then rolls over for 1023488. – Neil – 2018-07-07T12:47:01.450

2

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!

PieCot

Posted 2018-07-07T07:21:22.950

Reputation: 1 039

Sorry, I should have been more specific. You are outputting ’2GiB’ instead of ’2.00GiB’. – O.O.Balance – 2018-07-07T11:38:42.470

21023488 outputs 1e+03KiB, should probably be 0.98MiB. – None – 2018-07-07T12:47:10.850

@O.O.Balance, fixed ;). Thanks to point it out. – PieCot – 2018-07-07T19:50:56.370

@YiminRong I've updated the code to be adherent to the post author's guidelines – PieCot – 2018-07-07T19:52:45.263

1023488 --> 1000KiB – O.O.Balance – 2018-07-07T21:05:12.430

@O.O.Balance, fixed and added some extra corner cases. – PieCot – 2018-07-07T21:42:53.683

2

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);
}

Matej Mulej

Posted 2018-07-07T07:21:22.950

Reputation: 101

f(1023079) outputs 0.98MiB, output should probably be 999KiB. – None – 2018-07-07T11:53:02.303

@YiminRong Reading the question, I tend to disagree; I think this should be treated as an extension of the ’1000’ case. – O.O.Balance – 2018-07-07T11:59:14.040

You should remove the ’include’ from the header or add it to the byte count. It works fine without it though. – O.O.Balance – 2018-07-07T12:01:10.693

f(1023488) outputs 1000KiB for the first one, output should probably be 0.98MiB. Using >=999.5 in the while loop fixes it. – None – 2018-07-07T12:36:28.477

1

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

O.O.Balance

Posted 2018-07-07T07:21:22.950

Reputation: 1 499

You can replace String.format with "".format. – Jakob – 2018-07-07T22:27:26.083

@Jakob Thanks, indeed I can. I always forget that you can invoke static methods like that. – O.O.Balance – 2018-07-07T23:06:40.687

1

APL (Dyalog Classic), 60 bytes

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

Try it online!

ngn

Posted 2018-07-07T07:21:22.950

Reputation: 11 449

1Using 1023488 outputs 1000KiB, should probably be 0.98MiB. – None – 2018-07-07T12:42:19.317

@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). – ngn – 2018-07-07T17:20:12.257

1

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)

don bright

Posted 2018-07-07T07:21:22.950

Reputation: 1 189

A TIO link with some test cases would be nice. I always like to run the code :D – O.O.Balance – 2018-07-07T23:05:12.600

1https://bit.ly/2J7wseA – don bright – 2018-07-07T23:51:06.717

1

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

recursive

Posted 2018-07-07T07:21:22.950

Reputation: 8 616

0

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

WretchedLout

Posted 2018-07-07T07:21:22.950

Reputation: 395

@JoKing Oh. I knew I could do in[], but didn't think I could in'' – WretchedLout – 2018-07-07T10:28:51.997

Note that it doesn't match the format exactly, leaving out zeros after the decimal point. – O.O.Balance – 2018-07-07T10:29:46.537

@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. – WretchedLout – 2018-07-07T10:36:43.767

Well, it says: "You should output exactly the same to the testcase" – O.O.Balance – 2018-07-07T10:40:14.517

11023488 outputs 1E+03KiB, should probably be 0.98MiB. – None – 2018-07-07T12:49:31.840

0

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

user15259

Posted 2018-07-07T07:21:22.950

Reputation:

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: https://tio.run/##PctBC4IwAAXgvxIDYZvTNquDLBO6dIh@QXRYa7PBdKEmA/Gvt0Si43vvezKppAzh4d53q1aidxrKp2gx4rUwDTRkQEvGAx9/yBcLw3E8IG409HumNujVmqbXEETdERA8L8p2atSuhdwfijzP0x2PY4P8umA02/K/T7GOpJlPWQLnCllXMQo9Ip6AsizPlxO4mhvi0xRCYJTSj9RWVF1IbP0F

– O.O.Balance – 2018-07-07T12:48:01.190

@O.O.Balance -- Wow! I can never compete with the pros at golfing C! – None – 2018-07-07T12:56:02.190

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]);}}? – l4m2 – 2018-07-08T11:24:42.540

@O.O.Balance Still use double fine – l4m2 – 2018-07-09T00:25:37.517

>[C (gcc)], 155 bytes

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]);}}

Try it online!

– l4m2 – 2018-07-09T00:28:25.893

"???KMG"[i] => "KMG"[i-3]? – l4m2 – 2018-07-09T00:32:20.080

0

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

l4m2

Posted 2018-07-07T07:21:22.950

Reputation: 5 985