11
\$\begingroup\$

Background

The task is simple but every programmer has implemented it at least once. Stackoverflow has a lot of samples, but are they short enough to win?

Problem story

You're a grumpy programmer who was given the task of implementing file size input for the users. Since users don't use bytes everyone will just enter "1M", "1K", "3G", "3.14M" - but you need bytes! So you write a program to do the conversion.

Then your manager hands you dozens of user reports with complaints about weird large numbers in file size input. It seems you'll need to code the reverse conversion too.

What do you need to do?

Here's the trick: you should implement two-way conversion in single piece of code. Two separate functions used as needed? Nah, that's too simple - let's make one, short one!

For the purposes of this challenge, "kilobyte" means 1024 bytes, "megabyte" means 1024*1024 = 1048576 bytes, and "gigabyte" means 1024*1024*1024 = 1073741824 bytes.

Test data

Input   -> Output  
5       -> 5  
1023    -> 1023  
1024    -> 1K  
1K      -> 1024  
1.5K    -> 1536  
1536    -> 1.5K  
1048576 -> 1M  
1M      -> 1048576  

Rules

  • Test value will not exceed 2*1024*1024*1024 or 2G
  • The following postfixes are used by users: K for kilobytes, M for megabytes, G for gigabytes
  • Code is not required to work with negative numbers
  • Do not use any external libraries (e.g. BCMath in PHP) besides bundled ones (e.g. math.h)
  • Standard loopholes are disallowed
  • Code should not produce anything on stderr
  • Your program can take input and produce output using [standard methods].(http://meta.codegolf.stackexchange.com/questions/2447/default-for-code-golf-input-output-methods)
\$\endgroup\$
19
  • 1
    \$\begingroup\$ Usually programs don't take variable input and output... \$\endgroup\$
    – TheDoctor
    Commented Oct 22, 2015 at 22:58
  • 7
    \$\begingroup\$ 1024 is not a kilobyte (k); it's a kibibyte (KiB). Similarly, 1048576 is not a megabyte (M); it's a mebibyte (MiB). \$\endgroup\$ Commented Oct 22, 2015 at 23:03
  • 6
    \$\begingroup\$ Despite perhaps not being useful for real life, this challenge is well-specified and a non-trivial task. I for one think it just needs a little more polish on the wording. FYI, @kiler129, for future challenges you can get all these kinks worked out ahead of time by posting in the Sandbox first. \$\endgroup\$
    – DLosc
    Commented Oct 22, 2015 at 23:16
  • 2
    \$\begingroup\$ Possible duplicate: codegolf.stackexchange.com/questions/51928/… \$\endgroup\$ Commented Oct 22, 2015 at 23:45
  • 4
    \$\begingroup\$ how many decimal digits do you want? 1024 = 1k, 1025 = 1.0 k or 1k? 1526398 = 1.45568656921 M.... \$\endgroup\$
    – don bright
    Commented Oct 23, 2015 at 2:51

8 Answers 8

3
\$\begingroup\$

Python 2, 204 190 bytes

This is my first ever codegolf.

s=raw_input()
p=' KMG'
if s[-1]<='9':
 e=0;r=int(s)
 while r>=1024:r/=1024.0;e+=1
else:
 e=p.index(s[-1]);r=eval(s[:-1])
 while e>0:r*=1024;e-=1
print(`r`if r%1!=0 else`int(r)`)+p[e].strip()
\$\endgroup\$
1
  • \$\begingroup\$ -25 bytes. works by replacing the loop with the power operator (**) and changing the r%1!=0 to r%1 \$\endgroup\$ Commented May 15, 2020 at 4:00
2
\$\begingroup\$

Pip, 60 bytes

l:" KMG"Fi1,4Ia>=Y1024x:(a/:y).l@ix|aR`.+([KMG])`_*y**(l@?B)

Takes input from the command line (assigned to a variable) and outputs to stdout.

Ungolfed version:

l : " KMG"
Y 1024
F i 1,4
 I a>=y {
  a /: y
  x : a . l@i
 }
x | a R `.+([KMG])` {a*y**(l@?b)}

Algorithm:

  • For i from 1 through 3:
    • If a >= 1024, this is a byte value that needs to be converted to a larger unit:
      • Divide a by 1024
      • Set x to be the current value of a concatenated with the current unit
  • If x was set by the previous step, output it. Otherwise, a was less than 1024 (with a possible unit suffix), so:
    • Do a regex replacement with a function on a if it has one of KMG at the end: translate the letter to the appropriate power of 1024 and multiply the number by the result.
    • For values less than 1024 without a suffix, this leaves the value unchanged.
\$\endgroup\$
2
  • \$\begingroup\$ What is pip? Can you put a link or something? \$\endgroup\$
    – Wezl
    Commented May 15, 2020 at 17:12
  • 1
    \$\begingroup\$ Sure! Pip is a golfing language that I made. \$\endgroup\$
    – DLosc
    Commented May 16, 2020 at 15:35
1
\$\begingroup\$

JavaScript, 144 106 bytes

n=>((G=(M=(K=2<<9)<<10)<<10),+n>0?n<K?n:n<M?n/K+'K':n<G?n/M+'M':n/G+'G':eval(n.replace(/[KMG]/,m=>'*'+m)))

This can definitely be shortened, and will do later. Just wanted to get something ugly out there ;)

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

C 206 183 bytes

Got rid of as much as I could.

#include<stdlib.h>
main(y,z)char**z;{char*i="KMG";float f=atof(*++z);while(*++*z)y=**z;if(y>57){do f*=1024;while(y!=*i++);y=0;}else while(f>1023)y=*i++,f/=1024;printf("%.1lf%c",f,y);}

Output

Input: 1023
Output: 1023.0

Input: 1M
Output: 1048576.0

Input: 1048576
Output: 1.0M

Explanation

#include <stdlib.h> //needed for atof()
main(y,z)char **z; //y will hold the size indicator
{
    char *i="KMG";//holds the characters for the size indicators
    float f=atof(*++z); //get the number from the string

    while(*++*z) //loops through the string
        y=**z;
    if(y>57) //57 is ASCII for 9
    {
        do f*=1024;
        while(y!=*i++); //loop through until we hit the correct size
        y=0;
    }
    else
        while(f>1023) //loop through until number is less than 1024
            y=*i++,f/=1024; 
    printf("%.1lf%c",f,y); //print number and size character 
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ 136B \$\endgroup\$
    – l4m2
    Commented May 15, 2020 at 16:41
  • \$\begingroup\$ @l4m2, nice, you could edit that into the answer if you want (or post your own) \$\endgroup\$ Commented May 15, 2020 at 16:42
0
\$\begingroup\$

Python2 199 bytes

this is silly but it works (ignoring the decimal precision question) as long as you have dozens of gigabytes of RAM. dont run it otherwise, your machine might freeze and lockup. The first thing it does is allocate a 2gigabyte array of integers.

n,bn={'K':2**10,'M':2**20,'G':2**30},range(2**30+1)
for i in bn:
 for j in 'KMG':
  if i>=n[j]: bn[i]=str(float(i/n[j]))+j 
def f(i):
 if i[-1] in 'KMG': return n[i[-1]]*float(i[:-1])
 return bn[int(i)]
\$\endgroup\$
0
\$\begingroup\$

JavaScript (Node.js), 91 bytes

f=(n,m)=>n<1024?n+['.KMG'[m]]:n>0?f(n/1024,-~m):eval(n.replace(/.$/,'*32**$&',K=2,M=4,G=6))

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ @tsh If you use parseFloat(as there's case 1.5K they're same length) \$\endgroup\$
    – l4m2
    Commented May 15, 2020 at 4:19
0
\$\begingroup\$

Ruby -nl, 100 bytes

~/.$/
i,n,s,k=0,$_.to_f,' KMG',1024
$&<?A?((n/=k;i+=1)until n<k):n*=k**(s=~/#$&/)
print n,s[i].strip

Try it online!

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

C (gcc), 114 bytes

*i=L"KMG",d;c;main(S){float f;for(scanf(S="%f%c",&f,&c);c?f*=1024,c-*i++:f>1023?f/=1024,d=*i++:0;);printf(S,f,d);}

Try it online!

Thank ceilingcat for -11 bytes

\$\endgroup\$
2
  • \$\begingroup\$ @ceilingcat Fail on 512, output inf \$\endgroup\$
    – l4m2
    Commented May 16, 2020 at 2:03
  • \$\begingroup\$ @ceilingcat First argument of main is 1, but now the arg has its use \$\endgroup\$
    – l4m2
    Commented May 16, 2020 at 5:12

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.