Active Record Validation のRails Guideを読んでいて、気になったところがいくつかあったのでPRした。
上記Rails Guideを読んでいて、NumericalityValidator
がよくわからなかったので調べていた。
Rails Guideで次のように書かれているところに引っかかった。
それ以外の場合は、Floatを用いる数値の変換を試みます。Floatは、カラムの精度または15を用いてBigDecimalにキャストされます。
https://railsguides.jp/active_record_validations.html#numericality
原文だと以下のように書かれている
Otherwise, it will try to convert the value to a number using Float. Floats are casted to BigDecimal using the column's precision value or 15.
https://guides.rubyonrails.org/active_record_validations.html#numericality
ここでは、「値をFloatに変換したあと、BigDecimalに変換する」と書かれている。 Floatに変換した段階で情報が落ちるので、なぜ直接BigDecimalに変換しないのだろうと気になった。
この実装は次のあたりにある。
どうやら'0.12345'
のような値がこのvalidatorに渡ってきたときに、この「FloatにしてからBigDecimalにする」という処理を通りそうということがわかった。
経緯を追っていると、次のPRが見つかった。
このPRでは、もともとKernel.BigDecimal
を呼んでいたところで、Kernel.Float
を呼んだあとto_d
を呼ぶように変更されている。その意図は、to_f
を定義したPOROをこのvalidatorに渡したときにうまく動くようにするため、のよう。
この挙動については、意図した挙動のように見える。次のドキュメントでは「Precision of Kernel.Float values are guaranteed up to 15 digits.」と言っていて、15以上の精度が保証されているとは言っていない。
また、Active Record側のコードを見ると、精度をFloat::DIG
( == 15)で打ち切っている。
ということで、まあこれは意図した挙動なのではないかという気がしている。
実際これが問題になるのは、'0.1234567890123456789'
のような15桁を超える値がユーザーから送られてきて、かつそれを15桁を超える精度で比較したいときだけだと思う。validationという文脈ではそこまで細かい精度が必要になることはあまりない気がするので、問題にはなりづらい気がしている。
ちなみに、DB上ではこの精度は15より大きい値が許されているので(Floatより高い精度で計算したいのだからそれはそうか)、例えば20桁の精度で定義したカラムを持っているRails appとかはなにか問題を踏んだりしないのだろうか、とかはちょっと気になる。