# Number formatter

**URL:** <https://www.ruby-forum.com/t/number-formatter/110564>\
**Category:** Rails\
**Created:** [July 27, 2007, 3:50pm UTC](https://www.ruby-forum.com/t/number-formatter/110564 "2007-07-27T15:50:59Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [July 27, 2007, 3:50pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/1 "2007-07-27T15:50:59Z")

</div>

Hi,

Does anyone know if the reverse of the rails 20.kilobytes (megabytes/  
gigabytes etc.) exists? I have lots of values in my model that are  
stored as bytes and I’d like to easily convert them to a MB or GB  
approximation (say 1 decimal place) with \*B as a suffix after the  
number.

I also need to do the same for decimal numbers in terms of thousands  
and millions.

I could write something to do it but I wondered if there was an easier  
way?

Also if I did write a class to deal with it where in the rails  
directory structure should it go? helpers?

Thanks,  
Toby

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [July 27, 2007, 4:01pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/2 "2007-07-27T16:01:00Z")

</div>

On Jul 27, 6:50 am, “removed\_email\_address@domain.invalid” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> I could write something to do it but I wondered if there was an easier  
> way?
> 
> Also if I did write a class to deal with it where in the rails  
> directory structure should it go? helpers?
> 
> Thanks,  
> Toby

[http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Numeric/Bytes.html](http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Numeric/Bytes.html)

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [July 27, 2007, 4:09pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/3 "2007-07-27T16:09:00Z")

</div>

I am aware of the ruby extensions that will go in one direction but  
those don’t work in the other direction. All my values are bytes  
already and I want to convert those to kilo/mega/giga bytes etc.

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [July 27, 2007, 4:11pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/4 "2007-07-27T16:11:01Z")

</div>

> Also if I did write a class to deal with it where in the rails  
> directory structure should it go? helpers?

You could put it there if you think you’re unlikely to use it anywhere  
other than just this app, else I tend to put things like this into  
their own file in lib.

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [July 27, 2007, 4:58pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/5 "2007-07-27T16:58:07Z")

</div>

that was a fun little exercise… here’s how i interpreted your need:

class Numeric  
def to(unit, places=1)  
units = { :b =\> 1,  
:kb =\> 1024**1,  
:mb =\> 1024**2,  
:gb =\> 1024**3,  
:tb =\> 1024**4,  
:pb =\> 1024**5,  
:eb =\> 1024**6}  
unitval = units[unit.to\_s.downcase.to\_sym]  
“#{sprintf(”%.#{places}f", self / unitval)} #{unit.to\_s.upcase}"  
end # to  
end

puts 1024.to(:kb) # 1.0 KB  
puts 20000.0.to(:kb) # 19.5 KB  
puts 123456789.to(:mb) # 117.0 MB  
puts 123456789.to(‘MB’) # 117.0 MB  
puts 123456789.to(:MB) # 117.0 MB  
puts 123456789.to(:mb, 0) # 117 MB  
puts 345678912345.4.to(:gb, 3) # 321.939 GB

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [July 27, 2007, 5:12pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/6 "2007-07-27T17:12:17Z")

</div>

removed\_email\_address@domain.invalid wrote:

> I could write something to do it but I wondered if there was an easier  
> way?
> 
> Also if I did write a class to deal with it where in the rails  
> directory structure should it go? helpers?
> 
> Thanks,  
> Toby

I would just stick in in the core extensions as it’s very unlikely to  
conflict with anything and it’s “in the spirit” of how rails extends the  
core.

I don’t think you will find the reverse anywhere as the implementation  
is trivial

irb(main):001:0\> require ‘rubygems’  
=\> true  
irb(main):002:0\> gem ‘activesupport’  
=\> true  
irb(main):003:0\> require ‘active\_support’  
=\> true  
irb(main):004:0\> module ActiveSupport::CoreExtensions::Numeric::Bytes  
irb(main):005:1\> def to\_megabytes  
irb(main):006:2\> self/1.megabyte  
irb(main):007:2\> end  
irb(main):008:1\> end  
=\> nil  
irb(main):009:0\> num = 6.megabytes  
=\> 6291456  
irb(main):010:0\> num.to\_megabytes  
=\> 6

HTH.

ilan

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [July 27, 2007, 5:15pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/7 "2007-07-27T17:15:11Z")

</div>

Thanks that’s great - I did something similar in the end except mine  
didn’t have the ability to choose, it just went to the smallest that  
wasn’t less than zero. I think I’ll combine the two!

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [November 12, 2008, 7:31pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/8 "2008-11-12T19:31:44Z")

</div>

removed\_email\_address@domain.invalid wrote:

> Does anyone know if the reverse of the rails 20.kilobytes (megabytes/  
> gigabytes etc.) exists? I have lots of values in my model that are  
> stored as bytes and I’d like to easily convert them to a MB or GB  
> approximation (say 1 decimal place) with \*B as a suffix after the  
> number.

This reply is over a year old, but I thought I’d archive the information  
in this thread.

While ActionView in rails does have a helper for this, it requires you  
to supply the number of decimal places. What follows is my own code (as  
a Ramaze helper) that (by default) automatically determines the number  
of decimal places based on the size of the answer.

For example:  
[1500 12\_000 130\_000 1\_400\_000].each do |bytes|  
puts nice\_bytes( b )  
end  
#=\> 1.46kB  
#=\> 11.7kB  
#=\> 127kB  
#=\> 1.34MB

module Ramaze::Helper::NiceBytes  
K = 2.0**10  
M = 2.0**20  
G = 2.0**30  
T = 2.0**40  
def nice\_bytes( bytes, max\_digits=3 )  
value, suffix, precision = case bytes  
when 0…K  
[bytes, ‘b’, 0]  
else  
value, suffix = case bytes  
when K…M : [bytes / K, ‘kB’]  
when M…G : [bytes / M, ‘MB’]  
when G…T : [bytes / G, ‘GB’]  
else [bytes / T, ‘TB’]  
end  
used\_digits = case value  
when 0…10 : 1  
when 10…100 : 2  
when 100…1000 : 3  
end  
leftover\_digits = max\_digits - used\_digits  
[value, suffix, leftover\_digits \> 0 ? leftover\_digits : 0]  
end  
“%.#{precision}f#{suffix}” % value  
end  
end

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [July 27, 2007, 11:42pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/9 "2007-07-27T23:42:04Z")

</div>

You can use number\_to\_human\_size(size, precision=1) from  
ActionView::Helpers::NumberHelper  
([ActionView::Helpers::NumberHelper](http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#M000524))

Adam

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [February 12, 2009, 11:46am UTC](https://www.ruby-forum.com/t/number-formatter/110564/10 "2009-02-12T11:46:13Z")

</div>

Gavin K. wrote:

> While ActionView in rails does have a helper for this, it requires you  
> to supply the number of decimal places. What follows is my own code (as  
> a Ramaze helper) that (by default) automatically determines the number  
> of decimal places based on the size of the answer.

I just found this thread an Rails’ number\_to\_human\_size does exactly  
what you want as far as I can see, so there is no need to write your own  
function it seems?

> **[ActionView::Helpers::NumberHelper](https://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#M001656)**
>
> Provides methods for converting numbers into formatted strings. Methods are provided for phone numbers, currency, percentage, precision, positional notation, file size, and pretty printing.

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [February 13, 2009, 9:32am UTC](https://www.ruby-forum.com/t/number-formatter/110564/11 "2009-02-13T09:32:44Z")

</div>

> If you read the part of what I wrote that you quoted again, I think  
> you’ll find that Rails’ method does not do the same thing when no  
> precision parameter is supplied.

So what is so difficult or bad about giving it that one parameter  
:precision =\> 2 that you feel you have to roll your own lengthy  
function?

I still don’t understand, but I would like to.

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [February 13, 2009, 3:50am UTC](https://www.ruby-forum.com/t/number-formatter/110564/12 "2009-02-13T03:50:43Z")

</div>

Michael Hasenstein wrote:

> Gavin K. wrote:
> 
> > While ActionView in rails does have a helper for this, it requires you  
> > to supply the number of decimal places. What follows is my own code (as  
> > a Ramaze helper) that (by default) automatically determines the number  
> > of decimal places based on the size of the answer.
> 
> I just found this thread an Rails’ number\_to\_human\_size does exactly  
> what you want as far as I can see, so there is no need to write your own  
> function it seems?
> 
> [ActionView::Helpers::NumberHelper](http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#M001656)

If you read the part of what I wrote that you quoted again, I think  
you’ll find that Rails’ method does not do the same thing when no  
precision parameter is supplied.

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [February 13, 2009, 9:38am UTC](https://www.ruby-forum.com/t/number-formatter/110564/13 "2009-02-13T09:38:30Z")

</div>

Michael Hasenstein wrote:

> I still don’t understand, but I would like to.

Okay, now I do. But I still think a new function is not necessary. The  
only difference in behavior without the precision parameter is that the  
Rails function defaults to 1. Output of your example using the Rails  
function:

1.5 KB (your function: 1.46)  
11.7 KB  
127 KB  
1.3 MB (your function: 1.34)

That looks very acceptable to me and a lot of people. Of course, do what  
you want, the only reason I post is because when _I_ found this thread  
looking for such a functionality I copied your code, and only then  
realized there’s a function already. I respond to let others know right  
away who find this through Google.

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [February 13, 2009, 3:41pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/14 "2009-02-13T15:41:04Z")

</div>

Gavin K. wrote:

> #=\> 1b  
> #=\> 12b  
> #=\> 123b

[…]

> ```
> [bytes, 'b', 0]
> 
> ```

Ack! That’s twice now that I’ve pasted code in there with the incorrect  
lowercase ‘b’ for bytes. Should be:  
[bytes, ‘B’, 0]  
and thus  
#=\> 1B  
#=\> 12B  
#=\> 123B

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [February 13, 2009, 3:38pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/15 "2009-02-13T15:38:30Z")

</div>

Michael Hasenstein wrote:

> That looks very acceptable to me and a lot of people. Of course, do what  
> you want, the only reason I post is because when _I_ found this thread  
> looking for such a functionality I copied your code, and only then  
> realized there’s a function already. I respond to let others know right  
> away who find this through Google.

I think it’s excellent that you recorded for the record the simplest  
case for others to follow. To be clear, here’s the output I want (and  
have):

[ 1, 12, 123, 1234, 12345, 123456, 1234567, 12345678, 123456789,  
1234567890 ].each{ |bytes|  
puts nice\_bytes( bytes )  
}

#=\> 1b  
#=\> 12b  
#=\> 123b  
#=\> 1.21kB  
#=\> 12.1kB  
#=\> 121kB  
#=\> 1.18MB  
#=\> 11.8MB  
#=\> 118MB  
#=\> 1.15GB

With the exception of bytes, I get two decimal places when there are  
less than 10 of the amount, one decimal place with less than a hundred,  
and no decimal places for hundreds. Windows does this, and I actually  
like it. Although unrelated to real significant figures, it’s similar:  
there are ~always 3 digits displayed. As the number grows in magnitude,  
I am less interested in the details of the exact decimals.

When working with a large number of files all in the same rough range,  
this lets me automatically visually display them in a way that allows  
them to be compared and distinguished, without too much detail.

_shrug_

Not a big deal. As you say, most people will find the rails method suits  
their needs.

BTW, here’s updated 1.9 compatible code for the function:  
K = 2.0**10  
M = 2.0**20  
G = 2.0**30  
T = 2.0**40  
def nice\_bytes( bytes, max\_digits=3 )  
value, suffix, precision = case bytes  
when 0…K  
[bytes, ‘b’, 0]  
else  
value, suffix = case bytes  
when K…M then [bytes / K, ‘kB’]  
when M…G then [bytes / M, ‘MB’]  
when G…T then [bytes / G, ‘GB’]  
else [bytes / T, ‘TB’]  
end  
used\_digits = case value  
when 0…10 then 1  
when 10…100 then 2  
when 100…1000 then 3  
end  
leftover\_digits = max\_digits - used\_digits  
[value, suffix, leftover\_digits \> 0 ? leftover\_digits : 0]  
end  
“%.#{precision}f#{suffix}” % value  
end

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [February 13, 2009, 3:54pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/16 "2009-02-13T15:54:05Z")

</div>

Bah. One, final, 1.9-compatible post that also handles the edge case of  
values in the range 1000…1024 of a value correctly.

K = 2.0**10  
M = 2.0**20  
G = 2.0**30  
T = 2.0**40  
def nice\_bytes( bytes, max\_digits=3 )  
value, suffix, precision = case bytes  
when 0…K  
[bytes, ‘b’, 0]  
else  
value, suffix = case bytes  
when K…M then [bytes / K, ‘kB’]  
when M…G then [bytes / M, ‘MB’]  
when G…T then [bytes / G, ‘GB’]  
else [bytes / T, ‘TB’]  
end  
used\_digits = case value  
when 0…10 then 1  
when 10…100 then 2  
when 100…1024 then 3  
end  
leftover\_digits = max\_digits - used\_digits  
[value, suffix, leftover\_digits \> 0 ? leftover\_digits : 0]  
end  
“%.#{precision}f#{suffix}” % value  
end

[ 1, 12, 123, 1022, 1024,  
1234, 12345, 123456, 2 **19.99, 2** 20,  
1234567, 12345678, 123456789,  
1234567890  
].each{ |bytes| puts nice\_bytes( bytes ) }

#=\> 1b  
#=\> 12b  
#=\> 123b  
#=\> 1022b  
#=\> 1.00kB  
#=\> 1.21kB  
#=\> 12.1kB  
#=\> 121kB  
#=\> 1017kB  
#=\> 1.00MB  
#=\> 1.18MB  
#=\> 11.8MB  
#=\> 118MB  
#=\> 1.15GB

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [February 13, 2009, 10:19pm UTC](https://www.ruby-forum.com/t/number-formatter/110564/17 "2009-02-13T22:19:37Z")

</div>

Gavin K. wrote:

> Bah. One, final, 1.9-compatible post that also handles the edge case of  
> values in the range 1000…1024 of a value correctly.

Thanks for the effort!

Why don’t you submit it to Rails so that they replace their version with  
yours?
