-1

I am trying to round off the amounts i get to 2 decimal places. I have the amount attribute as a decimal field with precision 32 and scale 16.

I have tried doing amount.round(2)

def amount(amt)
  amt.to_f.round(2)
end

Let's say amt = 80500.00999999999. When printing this in rails logs, I get 80500.01. But when saving, it saves 80500.00999999999 in database. Also I've noted that if I do 65535.00999 (or smaller values), its rounding off and saving correctly to 65535.01. On the other hand, if am using the amount= 65536.00999 (or greater values), it saves the amount as it is in database without rounding. I took these numbers particularly since 65535 is the upper limit of unsigned smallInt in Postgres, and somehow am getting these results.

EDIT:

I have a Transaction model which has an attribute called amount. I have another file:

# app/operations/txn.rb

def create_txn
  @transaction = Transaction.new(transaction_params)
  @transaction.save
end

def transaction_params
{
  .
  .
  .
  amount: dest_amount,
  .
  .
  .
}
end

def dest_amount
  #logic
end

  • 2
    What exactly is your question? – Holger Just Jun 03 '19 at 11:25
  • 1
    The floating point value `80500.01` is an approximation. It's actual value is `80500.009999999994761310517787933349609375`. So if you say it is being saved as `80500.00999999999`, it seems right. Try to use a decimal value instead, i.e. [`BigDecimal`](http://ruby-doc.org/stdlib-2.6.3/libdoc/bigdecimal/rdoc/BigDecimal.html). – Stefan Jun 03 '19 at 11:25
  • @Stefan But how do you justify two different behaviors for two numbers? .round() is rounding off 65535.00999 to 65535.01, but not 65536.00999 – erum sanwari Jun 03 '19 at 11:42
  • Can you post the controller action, where the amount should be rounded and saved? – Roland Studer Jun 03 '19 at 12:03
  • @erumsanwari maybe I don't understand your problem. What values do you current have (in Ruby/Rails and in Postgres) and what are your expected values? – Stefan Jun 03 '19 at 13:44
  • @Stefan I have edited my question. I suppose this could be helpful. Thanks. – erum sanwari Jun 06 '19 at 06:21
  • @erumsanwari what's the value you have / want in your model and what's the value you have / want in your database? – Stefan Jun 06 '19 at 06:52
  • @Stefan I am getting the value from user. Now if I get value as 80500.00999999999 , then it should store the value as 80500.01 in database, which, I am not able to do with round(2). – erum sanwari Jun 06 '19 at 07:12
  • @erumsanwari as said in my first comment, the _float_ value `80500.01` (which is returned by `round`) is an approximation used for convenience. Its actual value (which is usually not shown to you) is `80500.009999999994761310517787933349609375` and that's what is being saved to your database. This is simply the way floats work. If you want a class that can represent decimal values exactly, use `BigDecimal`. – Stefan Jun 06 '19 at 07:21

2 Answers2

2

I will suggest you to put following setter as instance method for db attribute amount inside model,

def amount=(amt)
  super(amt.to_f.round(2))
end

It will save updated value of amount inside you database as per your need.

ray
  • 5,454
  • 1
  • 18
  • 40
  • @erumsanwari what is your database field and model name – ray Jun 03 '19 at 11:59
  • 2
    This would result in an endless loop. You should use `super` to pass the value to the original setter. `super(amt.to_f.round(2))` – 3limin4t0r Jun 03 '19 at 12:57
  • @3limin4t0r surprised by your comment, elaborate please. – ray Jun 03 '19 at 13:11
  • 1
    @ray I've created an [example](https://gist.github.com/3limin4t0r/9b27e0cfd3b8b6eba8c5c6df8570a81c), have a look. I hope this clears things up. – 3limin4t0r Jun 03 '19 at 13:59
  • @3limin4t0r thanks I can say you saved me! Also got [this](https://stackoverflow.com/a/10465235/10522579) – ray Jun 03 '19 at 14:06
  • @3limin4t0r I have edited my question to be more explanatory. Your solution, however isn't still helping me so I suppose now you could explain it properly... – erum sanwari Jun 06 '19 at 06:23
0

The usual method to solve the problems related to floats is to store cents as integers instead of dollars as floats.

You calculate and store everything as cents, and only need to convert to dollars for display purposes, by dividing by 100.0.

Eric Duminil
  • 52,989
  • 9
  • 71
  • 124