0

I have been using a state object in the database that keeps track of what seed data has been loaded into it. The structure of the table is:

create_table "toolkit_states", force: :cascade do |t|
  t.boolean "signups", default: true
  t.decimal "database_version", precision: 5, scale: 2
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

The seeds.rb file checks the database_version and runs blocks of code and then sets the database_version after running the block. It has worked fine from versions 0.1 up to 0.55.

I added a new block of seed data. To run that block the database_version is checked and it should be 0.56. The following comparison does not work:

if state.database_version == 0.56

For some reason, the number 0.56 cannot be evaluated for equality with the value stored in the database. It has worked on all the values up to 0.56.

Here is a rails console session:

irb(main):001:0> state = ToolkitState.first
ToolkitState Load (0.4ms)  SELECT  "toolkit_states".* FROM "toolkit_states" ORDER BY "toolkit_states"."id" ASC LIMIT $1  [["LIMIT", 1]]
=> #<ToolkitState id: 1, signups: false, database_version: 0.56e0, created_at: "2018-12-27 17:04:50", updated_at: "2018-12-27 17:04:56">
irb(main):002:0> state.database_version == 0.56
=> false
irb(main):003:0> state.database_version == 0.56e0
=> false
irb(main):004:0> state.database_version == 0.56.to_f
=> false
irb(main):005:0> state.database_version.to_f == 0.56
=> true

When I convert the value with a "to_f", the comparison works. My problem is that it as worked well without this conversion up to the value, 0.56

Dan S.
  • 162
  • 2
  • 13

2 Answers2

1

It occurs because state.database_version is an instance of BigDecimal class. This article explain why it is BigDecimal.

Look at this example:

BigDecimal('0.56e0')
=> 0.56e0
irb(main):008:0> BigDecimal('0.56e0') == 0.56
=> false
irb(main):009:0> BigDecimal('0.56e0').to_f
=> 0.56

As you can see 0.56e0 after transformation to float type becomes 0.56 and your comparison returns true.

Nate explained more briefly why it's happening in this comment.

Jan Matuszewski
  • 118
  • 1
  • 7
  • Why did the comparison work up until 0.56? Thanks for the information. I am just going to convert it before the comparison from now on. – Dan S. Jan 06 '19 at 19:30
0
irb(main):001:0>  c = BigDecimal('0.56e0')
=> 0.56e0
irb(main):002:0> c == 0.56
=> false
irb(main):003:0>  c = BigDecimal('0.55e0')
=> 0.55e0
irb(main):004:0> c == 0.55
=> true

Works for 0.55 and not for 0.56 Rails bug?

Dan S.
  • 162
  • 2
  • 13
  • 1
    BigDecimal and Float are both Ruby core classes, not Rails. This problem actually has nothing to do with Rails. This is likely a floating point precision inconsistency. Floats are always dangerous to compare. The following question has some details about how floats are less precise than BigDecimals and in some cases you end up with nearly the same but not exactly the same number as you expected. https://stackoverflow.com/q/3039650/10068463 – Nate Jan 06 '19 at 22:13