Tintin81
Tintin81

Reputation: 10207

How can this weird rounding error in Rails be fixed?

I recently came across this weird bug in Rails.

class PaymentsController < ApplicationController

  def new
    @payment = current_user.payments.build(:invoice_id => params[:invoice_id])
    @title = "Make payment" 
  end

end

class Payment < ActiveRecord::Base

  attr_accessible :amount, :date, :invoice_id

  after_initialize :set_amount

  private

  def set_amount
    if new_record? && invoice.present?
      self.amount ||= invoice.amount_payable
    end
  end

end

When I call this action from an invoice like so...

<%= link_to "Make payment", new_payment_path(:invoice_id => invoice.id) %>

...the payment form appears with the correct invoice preselected in the dropdown field (which is correct).

The payment amount is populated with the correct amount in about 90% of all cases.

However, sometimes it is not populated with the 2-digit amount_payable from the database, but rather some other weird value such as:

87.31999999999999

(where 87.32 is the decimal type value stored in the SQLite database)

Can somebody tell me what is causing this rounding error or point me in the right direction?

Thanks for any help.

By the way, this is my database schema:

create_table "invoices", :force => true do |t|
  t.decimal  "amount_payable", :precision => 8, :scale => 2
end

Upvotes: 0

Views: 606

Answers (1)

Jesse Wolgamott
Jesse Wolgamott

Reputation: 40277

You should never store Money as floats --- what's going on here is floating point arithmetic, and it's very common for calculations with money.

A better point is to store everything as integer for cents. So, you may have in your database a "amount_in_cents" column that is an integer.

Then, you add a getter/setter for 'amount' so you can use #amount all over the place.

class Payment < ActiveRecord::Base

  def amount
    amount_in_cents / 100.0
  end

  def amount=(other)
    self.amount_in_cents = (other * 100).to_i
  end
end

Upvotes: 5

Related Questions