7
\$\begingroup\$

Today computers use millions of bytes of memory, and decimal or hexadecimal notations get less and less informative.

Most human-readable formatting just display the number with the highest unit prefix. What about the rest?

I devised a notation that I use for myself, and since then, I'm often faced with re-implementing it in various languages. I thought I could share it, and that it make for a good case for code-golfing too.

The rules

  • The program is given an integer as its input. It can be taken from a decimal or hexadecimal representation in a text file and then parsed, or from the "system innards".
  • It should outputs a representation of that number somehow
  • The limits are the system's limit. For practical purposes, assume 64 bits unsigned, so that the maximum number is 0xFFFFFFFFFFFFFFFF, and the unit suffixes will be k (kibi), M (mebi), G (gibi), T (tebi), P (pebi), E (exbi).
  • zeros should be skipped. Example: 5G + 10k should be written 5G;10k, and not 5G;0M;10k;0
  • It frequently happens, e.g. for memory ranges, that a number is just one less than a number with a nicer representation. For instance, 0xffff would be displayed as 63k;1023, though it is 64k minus 1. An exclamation mark denotes this, so that 0xffff should be displayed as !64k.

Examples

  • 0 : 0
  • 1023: !1k
  • 1024: 1k
  • 1025: 1k;1
  • 4096: 4k
  • 0x100000 : 1M
  • 0xFFFFF : !1M
  • 0x20000f : 2M;15
  • 0x80003c00 : 2G;15k
  • 0xFFFFFFFFFFFFFFFF : !16E
    • For 32 bits users, use 0xFFFFFFFF, which is, as everybody knows, !4G.
    • And 128 bits users can go up to... wait that's big! Anyway, feel free...
\$\endgroup\$
  • \$\begingroup\$ Very similar on Stack Overflow: Code-Golf: Friendly Number Abbreviator. \$\endgroup\$ – dmckee --- ex-moderator kitten May 19 '11 at 15:24
  • 1
    \$\begingroup\$ Can an explanation mark occur in the middle of the sequence? \$\endgroup\$ – J B May 19 '11 at 17:58
  • \$\begingroup\$ What about 0xFFFFE? Can/should we write !!1M? \$\endgroup\$ – Howard May 19 '11 at 18:01
  • 1
    \$\begingroup\$ Howard: I'd say that's 1023k;1022, then. \$\endgroup\$ – Joey May 19 '11 at 19:23
  • 1
    \$\begingroup\$ You write "0xfff should be displayed as !64M", I suppose you mean "0xffff should be displayed as !64k" \$\endgroup\$ – asoundmove May 20 '11 at 4:28
2
\$\begingroup\$

Haskell, 335 333 332

f 0=["0"]
f n=let l=mod n 1024;s=f$div(n+1)1024;in if l==1023 then "!":s else show l:s
g []=[]
g ((_,"0"):s)=g s
g ((_,"!"):s)=let(c,z):t=g s in(c,'!':z):t
g ((c,n):s)=(c,n):g s
h ('_',s)=';':s
h (c,s)=';':(s++[c])
r n=let x=tail$concatMap h$reverse$g$zip"_kMGTPE"$f$read n in if x=="" then "0" else x
main=do x<-getLine;return$r x
\$\endgroup\$
2
\$\begingroup\$

Windows PowerShell, 207 214 226 230 247 271 319 339 360

$v=1PB*1KB
filter t{[Math]::Floor($n/$v)}$(,0*!($n=[Convert]::ToUInt64(($i="$input"),10+6*($i[1]-gt60)))
'EPTGMk'[0..5]|%{if($n%$v+1-eq$v){$v--
"!$(t)$_"
$n=0}if(t){"$(t)$_"
$n%=$v}$v/=1kb}
,$n*!!$n)-join';'

Test run:

./friendly2.ps1: PASS: '2047' -> '!2k'
./friendly2.ps1: PASS: '4096' -> '4k'
./friendly2.ps1: PASS: '0x80003c00' -> '2G;15k'
./friendly2.ps1: PASS: '2049' -> '2k;1'
./friendly2.ps1: PASS: '0x1' -> '1'
./friendly2.ps1: PASS: '4097' -> '4k;1'
./friendly2.ps1: PASS: '0xFFFFF' -> '!1M'
./friendly2.ps1: PASS: '0xa000' -> '40k'
./friendly2.ps1: PASS: '1022' -> '1022'
./friendly2.ps1: PASS: '0x100000' -> '1M'
./friendly2.ps1: PASS: '2048' -> '2k'
./friendly2.ps1: PASS: '0x20000f' -> '2M;15'
./friendly2.ps1: PASS: '0' -> '0'
./friendly2.ps1: PASS: '0x0' -> '0'
./friendly2.ps1: PASS: '1' -> '1'
./friendly2.ps1: PASS: '0x80aef3' -> '8M;43k;755'
./friendly2.ps1: PASS: '14636699861675008' -> '13P;1023M;27k'
./friendly2.ps1: PASS: '0xFFFFFFFFFFFFFFFe' -> '15E;1023P;1023T;1023G;1023M;1023k;1022'
./friendly2.ps1: PASS: '1023' -> '!1k'
./friendly2.ps1: PASS: '4095' -> '!4k'
./friendly2.ps1: PASS: '0xFFFFFFFFFFFFFFFF' -> '!16E'
./friendly2.ps1: PASS: '1025' -> '1k;1'
./friendly2.ps1: PASS: '1024' -> '1k'
\$\endgroup\$
  • \$\begingroup\$ +1 for a good list of test cases - however how about adding one more test case? I suggest 0xFF0000FFFF. \$\endgroup\$ – asoundmove May 22 '11 at 19:50
  • \$\begingroup\$ @asoundmove: Most of those were already present in the question anyway. I just made them into a script to automatically test my solution. \$\endgroup\$ – Joey May 22 '11 at 21:16
1
\$\begingroup\$

Scala, 295 characters

object F extends App{type S=String;def f(l:BigInt,s:S,p:S,g:Int,q:Int):S={val j=l&1023;return if(l+g>0)f(l>>10,if((j+g<1024&&j+g-q>0)||l-q<0)";"+"!"*(g-q)+(j+g-q)+p(0)+s else s,p.tail,(j.toInt+g)/1024,0)else s};val x=readLine.split('x');print(f(BigInt(x.last,4+x.size*6),""," kMGTPE",1,1).tail)}

With some line breaks to aid readability (though not much):

object F extends App
{
    type S=String
    def f(l:BigInt,s:S,p:S,g:Int,q:Int):S=
    {
        val j=l&1023
        return if(l+g>0)f(l>>10,if((j+g<1024&&j+g-q>0)||l-q<0)";"+"!"*(g-q)+(j+g-q)+p(0)+s else s,p.tail,(j.toInt+g)/1024,0)else s
    }
    val x=readLine.split('x')
    print(f(BigInt(x.last,4+x.size*6),""," kMGTPE",1,1).tail)
}
\$\endgroup\$
1
\$\begingroup\$

JavaScript, 461 452 423 269 267 247

function(n){for(k=1024,M=k*k,G=M*k,T=G*k,P=T*k,E=P*k,y='EPTGMk',r='';n;){for(i=-1,f=0;++i<6;)n==(v=eval(x=y[i]))-1?(r+='!1'+x,n=0,f=1):n>=v?(r+=Math.floor(n/v)+x+';',n%=v,f=1):0;f?0:(r+=n,n=0);}return r?r[l=r.length-1]==';'?r.substring(0,l):r:'0'}
\$\endgroup\$
  • 2
    \$\begingroup\$ Hey you could shorten that a fair bit (2+4+6+8 = 20 bytes) with G=M*k... \$\endgroup\$ – asoundmove May 20 '11 at 4:21
1
\$\begingroup\$

Ruby 1.9, 130 167 181 191 203 characters

n=eval gets
u=1023
x=n&u<u ?n<1??0:"":(n+=1;?!)
s=[]
6.downto(0){|i|a=1024**i;n/a>0&&s<<[n/a,"EPTGMk"[6-i]]*""&&n=n%a}
puts x+s*?;

Passes all testcases from Joey's answer.

\$\endgroup\$
  • \$\begingroup\$ Can you not save 1 more char with n+=1 to n++? \$\endgroup\$ – asoundmove May 22 '11 at 19:37
  • \$\begingroup\$ @asoundmove: Ruby doesn't have increment operators, so the shortest way to increment a variable by one is indeed a+=1. \$\endgroup\$ – Ventero May 22 '11 at 19:40
0
\$\begingroup\$

C#, 302 323

Direct conversion of my PowerShell solution.

using System;class F{static void
Main(){var
s=Console.ReadLine();ulong
n=Convert.ToUInt64(s,s.IndexOf('x')==1?16:10),v=1L<<60,b;s=n<1?"0":"";foreach(var
x in "EPTGMk"){b=0;if(n%v==v-1){s+="!"+(n/--v)+x;n=0;}if(n/v>0){s=s+(n/v)+x;n%=v;b=1;}if(b>0&&n>0)s+=';';v/=1024;}if(n>0)s+=n;Console.Write(s);}}
\$\endgroup\$
0
\$\begingroup\$

Python, 138 chars

N=input()
M=1023
s='0'[N:]
if N&M==M:N+=1;s='!'
print s+';'.join('%d'%b+k for b,k in zip([N>>60-10*i&M for i in range(7)],'EPTGMk ')if b)
\$\endgroup\$
  • \$\begingroup\$ Wouldn't that output a trailing space? \$\endgroup\$ – Joey May 20 '11 at 23:32
  • 1
    \$\begingroup\$ @Joey: Yes, it does sometimes. Add a .strip() for 8 more characters if you care. \$\endgroup\$ – Keith Randall May 20 '11 at 23:43

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.