Files
fixin.me/DESIGN.md
cryptogopher 5d051de666 Check for out of range float values
Remove unsupported attribute `[maxlength]` from `input[number]`.
2026-07-20 21:45:33 +02:00

4.4 KiB

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,
      • if a decimal string with at most 15 significant digits is converted to the IEEE 754 double-precision format, giving a normal number, and then converted back to a decimal string with the same number of digits, the final result should match the original string,
      • if an IEEE 754 double-precision number is converted to a decimal string with at least 17 significant digits, and then converted back to double-precision representation, the final result must match the original number,
      • 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 majority of 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,
    • at present, only normalized numbers are used and considered sufficient, in order to avoid potential issues with the cross-platform compatibility of subnormal (denormal) numbers,
  • in the future, the IEEE 754 decimal64 data type may be considered once it is supported by database engines.

Database layer vs application layer data model constraints

  • database constraints are the final guard against data integrity corruption,
    • they should safeguard against data referential integrity loss under all data (not schema) manipulation scenarios, including application level logic errors and direct data manipulation (e.g. through dbconsole),
  • application constraints can be as restrictive as database constraints or more, but not less, as it doesn't serve any use case,
    • proper application level constraints should prevent unhandled database exception occurences, e.g ActiveRecord::InvalidForeignKey for operations performed through Models (i.e. not #delete_all etc.)

User interface

  • values provided by user have to be exactly preserved, avoiding any precision loss due to e.g. type casting
    • when it's not possible to store value without information loss, feedback has to be given at the time of entering value
    • values can be received using either:
      • input[number] - validates: number format, value against [min] and [max] attributes; cannot validate precision (no [pattern] support); or
      • input[text] - can validate number format and precision with [pattern]; cannot validate range (no [min]/[max] support),
      • none of the above prevents misinterpretation of values between 0 and Float::MIN as 0.0 - this has to be checked manually,
      • assuming that user input with precision > DIG is as rare as in the range (0, MIN), the decision is to choose input which requires the least/simplest additional code.