# Formatting numbers

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...
• Very similar on Stack Overflow: Code-Golf: Friendly Number Abbreviator. May 19, 2011 at 15:24
• Can an explanation mark occur in the middle of the sequence?
– J B
May 19, 2011 at 17:58
• What about 0xFFFFE? Can/should we write !!1M? May 19, 2011 at 18:01
• Howard: I'd say that's 1023k;1022, then.
– Joey
May 19, 2011 at 19:23
• You write "0xfff should be displayed as !64M", I suppose you mean "0xffff should be displayed as !64k" May 20, 2011 at 4:28

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


## Windows PowerShell, 207 214226230247271319339360

$v=1PB*1KB filter t{[Math]::Floor($n/$v)}$(,0*!($n=[Convert]::ToUInt64(($i="$input"),10+6*($i-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'

• +1 for a good list of test cases - however how about adding one more test case? I suggest 0xFF0000FFFF. May 22, 2011 at 19:50
• @asoundmove: Most of those were already present in the question anyway. I just made them into a script to automatically test my solution.
– Joey
May 22, 2011 at 21:16

## 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
}
print(f(BigInt(x.last,4+x.size*6),""," kMGTPE",1,1).tail)
}


# JavaScript, 461452423269267 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'}

• Hey you could shorten that a fair bit (2+4+6+8 = 20 bytes) with G=M*k... May 20, 2011 at 4:21

## 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.

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

## C#, 302 323

Direct conversion of my PowerShell solution.

using System;class F{static void
Main(){var
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);}}


## 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)

• Wouldn't that output a trailing space?
– Joey
May 20, 2011 at 23:32
• @Joey: Yes, it does sometimes. Add a .strip() for 8 more characters if you care. May 20, 2011 at 23:43