forked from fixin.me/fixin.me
Drop Readout.value decimal type in favor of float
This commit is contained in:
34
DESIGN.md
Normal file
34
DESIGN.md
Normal file
@@ -0,0 +1,34 @@
|
||||
DESIGN
|
||||
======
|
||||
|
||||
Below is a list of design decisions. The justification is to be consulted
|
||||
whenever a change is considered, to avoid regressions.
|
||||
|
||||
### Data type for DB storage of numeric values (`decimal` vs `float`)
|
||||
|
||||
* among database engines supported (by Rails), SQLite offers storage of
|
||||
`decimal` data type with the lowest precision, equal to the precision of
|
||||
`REAL` type (double precision float value, IEEE 754), but in a floating point
|
||||
format,
|
||||
* decimal types in other database engines offer greater precision, but store
|
||||
data in a fixed point format,
|
||||
* biology-related values differ by several orders of magnitude; storing them in
|
||||
fixed point format would only make sense if required precision would be
|
||||
greater than that offered by floating point format,
|
||||
* even then, fixed point would mean either bigger memory requirements or
|
||||
worse precision for numbers close to scale limit,
|
||||
* for a fixed point format to use the same 8 bytes of storage as IEEE
|
||||
754, precision would need to be limited to 18 digits (4 bytes/9 digits)
|
||||
and scale approximately half of that - 9,
|
||||
* double precision floating point guarantees 15 digits of precision, which
|
||||
is more than enough for all expected use cases,
|
||||
* single precision floating point only guarntees 6 digits of precision,
|
||||
which is estimated to be too low for some use cases (e.g. storing
|
||||
latitude/longitude with a resolution grater than 100m)
|
||||
* double precision floating point (IEEE 754) is a standard that ensures
|
||||
compatibility with all database engines,
|
||||
* the same data format is used internally by Ruby as a `Float`; it
|
||||
guarantees no conversions between storage and computation,
|
||||
* as a standard with hardware implementations ensures both: computing
|
||||
efficiency and hardware/3rd party library compatibility as opposed to Ruby
|
||||
custom `BigDecimal` type
|
||||
@@ -102,13 +102,17 @@ module ApplicationHelper
|
||||
|
||||
def number_field(method, options = {})
|
||||
attr_type = object.type_for_attribute(method)
|
||||
if attr_type.type == :decimal
|
||||
case attr_type.type
|
||||
when :decimal
|
||||
options[:value] = object.public_send(method)&.to_scientific
|
||||
options[:step] ||= BigDecimal(10).power(-attr_type.scale)
|
||||
options[:max] ||= BigDecimal(10).power(attr_type.precision - attr_type.scale) -
|
||||
options[:step]
|
||||
options[:min] = options[:min] == :step ? options[:step] : options[:min]
|
||||
options[:min] ||= -options[:max]
|
||||
options[:size] ||= attr_type.precision / 2
|
||||
when :float
|
||||
options[:size] ||= 6
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
<%= form.hidden_field :quantity_id %>
|
||||
</td>
|
||||
<td>
|
||||
<%= form.number_field :value, required: true,
|
||||
size: readout.type_for_attribute(:value).precision / 2,
|
||||
autofocus: readout_counter == 0 %>
|
||||
<%= form.number_field :value, required: true, autofocus: readout_counter == 0 %>
|
||||
</td>
|
||||
<td>
|
||||
<%= form.collection_select :unit_id, @user_units, :id,
|
||||
|
||||
@@ -3,8 +3,10 @@ class CreateReadouts < ActiveRecord::Migration[7.2]
|
||||
create_table :readouts do |t|
|
||||
t.references :user, null: false, foreign_key: true
|
||||
t.references :quantity, null: false, foreign_key: true
|
||||
# :category + :value + :unit as a separate table? (NumericValue, TextValue)
|
||||
t.integer :category, null: false, default: 0
|
||||
t.float :value, null: false, limit: Float::MANT_DIG
|
||||
t.references :unit, foreign_key: true
|
||||
t.decimal :value, null: false, precision: 30, scale: 15
|
||||
#t.references :collector, foreign_key: true
|
||||
#t.references :device, foreign_key: true
|
||||
|
||||
|
||||
@@ -28,8 +28,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_01_21_230456) do
|
||||
create_table "readouts", charset: "utf8mb4", collation: "utf8mb4_0900_as_ci", force: :cascade do |t|
|
||||
t.bigint "user_id", null: false
|
||||
t.bigint "quantity_id", null: false
|
||||
t.integer "category", default: 0, null: false
|
||||
t.float "value", limit: 53, null: false
|
||||
t.bigint "unit_id"
|
||||
t.decimal "value", precision: 30, scale: 15, null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["quantity_id", "created_at"], name: "index_readouts_on_quantity_id_and_created_at", unique: true
|
||||
|
||||
Reference in New Issue
Block a user