Rails 3. How to Explicitly Round a Number to Two Decimal Places in The Model

Rails 3. How to explicitly round a number to two decimal places in the model?

You should try using the :decimal type of database field, with the :scale set to 2

To add a decimal column to a new table;

create_table :my_table do |t|
t.decimal :my_column, :scale => 2
end

To add a column to an existing table;

add_column :my_table, :my_column, :decimal, :scale => 2

It is wise to have precisions since some database does not have precision defaults. Such as postgresql:

 add_column :my_table, my_column, precision: 30, scale: 2

Rails 3. How to display two decimal places in edit form?

You should use number_with_precision helper. See doc.

Example:

number_with_precision(1.5, :precision => 2)
=> 1.50

Within you form helper:

<%= f.text_field :cost, :class => 'cost', :value => (number_with_precision(f.object.cost, :precision => 2) || 0) %>

BTW, if you really want to display some price, use number_to_currency, same page for doc (In a form context, I'd keep number_with_precision, you don't want to mess up with money symbols)


How to round an average to 2 decimal places in PostgreSQL?

PostgreSQL does not define round(double precision, integer). For reasons @Mike Sherrill 'Cat Recall' explains in the comments, the version of round that takes a precision is only available for numeric.

regress=> SELECT round( float8 '3.1415927', 2 );
ERROR: function round(double precision, integer) does not exist

regress=> \df *round*
List of functions
Schema | Name | Result data type | Argument data types | Type
------------+--------+------------------+---------------------+--------
pg_catalog | dround | double precision | double precision | normal
pg_catalog | round | double precision | double precision | normal
pg_catalog | round | numeric | numeric | normal
pg_catalog | round | numeric | numeric, integer | normal
(4 rows)

regress=> SELECT round( CAST(float8 '3.1415927' as numeric), 2);
round
-------
3.14
(1 row)

(In the above, note that float8 is just a shorthand alias for double precision. You can see that PostgreSQL is expanding it in the output).

You must cast the value to be rounded to numeric to use the two-argument form of round. Just append ::numeric for the shorthand cast, like round(val::numeric,2).


If you're formatting for display to the user, don't use round. Use to_char (see: data type formatting functions in the manual), which lets you specify a format and gives you a text result that isn't affected by whatever weirdness your client language might do with numeric values. For example:

regress=> SELECT to_char(float8 '3.1415927', 'FM999999999.00');
to_char
---------------
3.14
(1 row)

to_char will round numbers for you as part of formatting. The FM prefix tells to_char that you don't want any padding with leading spaces.

Rounding in Ruby (Rails)

You just need to pass a correct datatype into helper.

Instead of feeding it Integer instance (995 / 100 gives you integer 9), pass a Float instance (995 / 100.to_f, which gives you Float 9.95):

number_with_precision (995 / 100.to_f, precision: 2) # or 995 / 100.0
#=> 9.95

What is the best method of handling currency/money?

You'll probably want to use a DECIMAL type in your database. In your migration, do something like this:

# precision is the total number of digits
# scale is the number of digits to the right of the decimal point
add_column :items, :price, :decimal, :precision => 8, :scale => 2

In Rails, the :decimal type is returned as BigDecimal, which is great for price calculation.

If you insist on using integers, you will have to manually convert to and from BigDecimals everywhere, which will probably just become a pain.

As pointed out by mcl, to print the price, use:

number_to_currency(price, :unit => "€")
#=> €1,234.01

Decimal values are truncating with to_f

Can I know what will be the reason the to_f automatically reduce the limit of the decimal?

The reason is the to_f methods are used to convert objects to Floats, which are standard 64-bit double precision floating point numbers. The precision of these numbers is limited, therefore the precision of the original object must be automatically reduced during the conversion process in order to make it fit in a Float. All extra precision is lost.

It looks like you are using the BigDecimal class. The BigDecimal#to_f method will convert the arbitrary precision floating point decimal object into a Float. Naturally, information will be lost during this conversion should the big decimal be more precise than what Floats allow. This conversion can actually overflow or underflow if limits are exceeded.

I was just thinking about some truncate with some limit

There is a truncate method if you'd like explicit control over the precision of the result. No rounding of any kind will occur, there is a separate method for that.

  • BigDecimal#truncate

    Deletes the entire fractional part of the number, leaving only an integer.

    BigDecimal('3.14159').truncate #=> 3
  • BigDecimal#truncate(n)

    Keeps n digits of precision, deletes the rest.

    BigDecimal('3.14159').truncate(3) #=> 3.141

How do I configure Rails to output decimals to the correct precision in form fields?

I ended up writing a plugin to do this: currency_text_field.

You can define your formats (arguments to number_with_precision) in config/initializers/currency_text_field_initializer.rb.

Then you use f.currency_text_field :rate in your form, with an optional :format argument to use a named format in the initializer. Otherwise it uses the format[:default] from the initializer.

Same approach as setting the value explicitly, but does it all behind the scenes and has named sets of options to number_with_precision.



Related Topics



Leave a reply



Submit