0

I am working on reports for a website and I am currently thinking of what would be the best way to handle BigDecimal -0.0's.

The database I'm working with has a lot of them. When these -0.0's are put through number_to_currency(), I get "$-0.00". My format for negative numbers is actually "-$x.xx", so note that number_to_currency is not formatting it as a negative number (otherwise there would also be a negative sign in front of the dollar sign), but for some reason the negative sign is being translated along with the 0.

Right now my solution is to do this every time I get an amount from the database:

amount *= -1 if amount == 0 && amount.sign == -1

This changes the -0.0 to a 0.0. It's simple enough, but I can't help but wonder if there is a better solution, or something on BigDecimals or number_to_currency to handle this situation that I'm just not finding.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Isaac
  • 2,246
  • 5
  • 21
  • 34
  • 1
    What calculation are you doing to store $-0.0 in your database? When I do `(BigDecimal.new("50.00") + BigDecimal.new("-50.00")).zero?` I get `true` – Anthony Dec 19 '14 at 15:59
  • @Anthony - I don't know how the -0.0 is stored in the database. I'm working with a database I didn't create, buy my next step will be to investigate this. If I can avoid storing it, it will save me a lot of work. Otherwise, Syed has given me a pretty thorough answer and a nicer way to write my solution. – Isaac Dec 19 '14 at 17:00
  • 1
    If you have any say, I'd talk to the team, write a migration to convert all the -0.0 to 0.0 in the database and then not have to worry about any of this view logic. Obviously that requires you store it correctly in the db going forward too. – Anthony Dec 19 '14 at 17:14
  • @Anthony - It was being stored fine as 0.0, but one of the models applied a *-1 if the transaction was of type 'CREDIT'. I've added a check in the method to first see if it is .zero? All of my "$-0.00"s are gone now :) – Isaac Dec 19 '14 at 17:34

1 Answers1

2

That is so because the number is converted into a string to be displayed. And:

# to_d converts to BigDecimal, just FYI
"-0".to_d.to_s #=> "-0.0"

Therefore you will have to make it a 0 yourself. But the sign-checks are redundant - a simple comparison with 0 will do the trick:

bdn = "-0".to_d # or BigDecimal.new("-0")
value = bdn.zero? ? 0 : bdn
number_to_currency(value, other_options)

However, you wouldn't want to manually add this check everywhere you're calling number_to_currency. It would be more convenient to create your own modified_number_to_currency method, in your ApplicationHelper, like so:

def modified_number_to_currency( number, options )
  value = number.zero? ? 0 : number
  number_to_currency(value, options)
end

And then use modified_number_to_currency instead of number_to_currency.

Alternatively, you could overwrite number_to_currency and have it call super in the end. That might also work but I'm not 100% certain.

Coming to your check specifically:

amount *= -1 if amount == 0 && amount.sign == -1

It should simply be:

amount = 0.to_d if amount.zero? # the to_d might or might not be required
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
SHS
  • 7,651
  • 3
  • 18
  • 28
  • This is awesome. Thank you. You confirmed a lot of the stuff that I was thinking and made my check even better. And the .to_d is not required. I am going to try and investigate how these BigDecimals are being stored in the database and hopefully I can avoid storing -0's. That would avoid the need for a check altogether. – Isaac Dec 19 '14 at 16:59
  • It was being stored fine as 0.0, but one of the models applied a *-1 if the transaction was of type 'CREDIT'. I've added a check in the method to first see if it is .zero? All of my "$-0.00"s are gone now :) – Isaac Dec 19 '14 at 17:35
  • When using `modified_number_to_currency` it is good to keep the default options of `options = {}` or one may run into troubles when no options are given. – silverdr Nov 28 '16 at 23:24