I\'m trying to make a method that converts an integer that represents bytes to a string with a \'prettied up\' format.
Here\'s my half-working attempt:
This is my solution:
def filesize(size)
units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'Pib', 'EiB']
return '0.0 B' if size == 0
exp = (Math.log(size) / Math.log(1024)).to_i
exp += 1 if (size.to_f / 1024 ** exp >= 1024 - 0.05)
exp = 6 if exp > 6
'%.1f %s' % [size.to_f / 1024 ** exp, units[exp]]
end
Compared to other solutions it's simpler, more efficient, and generates a more proper output.
Both to_filesize
and to_human
have issues with big numbers. format_mb
has a weird case where for example '1 MiB' is considered '1024 KiB' which is something some people might want, but certainly not me.
origin: filesize to_filesize format_mb to_human
0 B: 0.0 B 0.0B 0 b 0.00 B
1 B: 1.0 B 1.0B 1 b 1.00 B
10 B: 10.0 B 10.0B 10 b 10.00 B
1000 B: 1000.0 B 1000.0B 1000 b 1000.00 B
1 KiB: 1.0 KiB 1.0KB 1024 b 1.00 KB
1.5 KiB: 1.5 KiB 1.5KB 1536.0 b 1.50 KB
10 KiB: 10.0 KiB 10.0KB 10.000 kb 10.00 KB
100 KiB: 100.0 KiB 100.0KB 100.000 kb 100.00 KB
1000 KiB: 1000.0 KiB 1000.0KB 1000.000 kb 1000.00 KB
1 MiB: 1.0 MiB 1.0MB 1024.000 kb 1.00 MB
1 Gib: 1.0 GiB 1.0GB 1024.000 mb 1.00 GB
1 TiB: 1.0 TiB 1.0TB 1024.000 gb 1.00 TB
1 PiB: 1.0 Pib ERROR 1024.000 tb 1.00 PB
1 EiB: 1.0 EiB ERROR 1024.000 pb 1.00 EB
1 ZiB: 1024.0 EiB ERROR 1024.000 eb ERROR
1 YiB: 1048576.0 EiB ERROR 1048576.000 eb ERROR
Also, it has the best performance.
user system total real
filesize: 2.740000 0.000000 2.740000 ( 2.747873)
to_filesize: 3.560000 0.000000 3.560000 ( 3.557808)
format_mb: 2.950000 0.000000 2.950000 ( 2.949930)
to_human: 5.770000 0.000000 5.770000 ( 5.783925)
I tested each implementation with a realistic random number generator:
def numbers
Enumerator.new do |enum|
1000000.times do
exp = rand(5)
num = rand(1024 ** exp)
enum.yield num
end
end
end