経緯
競技プログラミングでMath.sqrtだと精度が足りず不正解になるが、Integer.sqrtだと正解になる設問がありました。なのでこの2つをどう使い分けるべきか考えてみました。
※ ちなみに対象の設問はAtCoder Beginner Contest 191の「D - Circle Lattice Points」
結論
実行環境によるが、小数点以下15桁より小さい部分まで精度が必要な場合はInteger.sqrtのほうが良い。なお、Integer.sqrtは整数型を返すので、例えば2の平方根を求める場合は以下のように下駄を履かせる必要がある。
U = 10 ** 20
"%.20f" % Integer.sqrt(2 * (U ** 2)).quo(U)
=> "1.41421356237309504880"
# Wikipediaで調べた2の平方根と同じ値
"%.20f" % Math.sqrt(2)
=> "1.41421356237309514547"
# 下5桁の値が違う
調査内容
リファレンスマニュアルのInteger::sqrtによると、
Math.sqrt(n).floor と同等ですが、後者は浮動小数点数の精度の限界によって真の値とは違う結果になることがあります。
Integer.sqrt(10**46) #=> 100000000000000000000000
Math.sqrt(10**46).floor #=> 99999999999999991611392 (!)
また、リファレンスマニュアルのclass Floatによると、
Float の実装は C 言語の double で、その精度は環境に依存します。一般にはせいぜい15桁です。
実際にirbで試してみると、下4桁がInteger.sqrtは正しいですが、Math.sqrtは不正確な値となっています。
> U = 10 ** 20
=> 100000000000000000000
> "%d" % Math.sqrt(2 * (U ** 2))
=> "141421356237309509632"
> "%d" % Integer.sqrt(2 * (U ** 2))
=> "141421356237309504880"
※ Wikipediaによると2の平方根は1.4142135623730950「4880」168872420969807856967187537694807317667973799073247846210703885038753432764157…