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 = {})
|
def number_field(method, options = {})
|
||||||
attr_type = object.type_for_attribute(method)
|
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[:value] = object.public_send(method)&.to_scientific
|
||||||
options[:step] ||= BigDecimal(10).power(-attr_type.scale)
|
options[:step] ||= BigDecimal(10).power(-attr_type.scale)
|
||||||
options[:max] ||= BigDecimal(10).power(attr_type.precision - attr_type.scale) -
|
options[:max] ||= BigDecimal(10).power(attr_type.precision - attr_type.scale) -
|
||||||
options[:step]
|
options[:step]
|
||||||
options[:min] = options[:min] == :step ? options[:step] : options[:min]
|
options[:min] = options[:min] == :step ? options[:step] : options[:min]
|
||||||
options[:min] ||= -options[:max]
|
options[:min] ||= -options[:max]
|
||||||
|
options[:size] ||= attr_type.precision / 2
|
||||||
|
when :float
|
||||||
|
options[:size] ||= 6
|
||||||
end
|
end
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,9 +11,7 @@
|
|||||||
<%= readout.quantity.relative_pathname(@superquantity) %>
|
<%= readout.quantity.relative_pathname(@superquantity) %>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<%= form.number_field :value, required: true,
|
<%= form.number_field :value, required: true, autofocus: readout_counter == 0 %>
|
||||||
size: readout.type_for_attribute(:value).precision / 2,
|
|
||||||
autofocus: readout_counter == 0 %>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<%= form.hidden_field :quantity_id %>
|
<%= form.hidden_field :quantity_id %>
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
class CreateReadouts < ActiveRecord::Migration[7.2]
|
class CreateReadouts < ActiveRecord::Migration[7.2]
|
||||||
def change
|
def change
|
||||||
create_table :readouts do |t|
|
create_table :readouts do |t|
|
||||||
t.references :user, null: false, foreign_key: true
|
# Reference :user through :quantity (:measurement may be NULL).
|
||||||
|
t.references :measurement, foreign_key: true
|
||||||
t.references :quantity, 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.references :unit, foreign_key: true
|
||||||
t.decimal :value, null: false, precision: 30, scale: 15
|
# Move to Measurement?
|
||||||
#t.references :collector, foreign_key: true
|
#t.references :collector, foreign_key: true
|
||||||
#t.references :device, foreign_key: true
|
#t.references :device, foreign_key: true
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user