えびちゃんです。お気持ち表明記事です。最近「お気持ち表明」というフレーズをあまり聞かなくなった気がしますね。
各章はそれぞれ基本的に独立です。特に結論めいたものはありません。
出会い
えびちゃんが競プロを始めたのは 2016 年頃で、それ以来なんやかんやで浮動小数点型には「ブラックボックス」感を抱いていた記憶があります。
競プロ界隈は別に浮動小数点型に詳しい界隈ではないので、(#define EPS 1e-6 などに代表されるような)怪しい民間療法のようなものが当時から信仰されていました。
「できる限り整数型で処理できるように式変形・考察しましょう」という定石は全く正しいと思っていますが、浮動小数点型への忌避意識のようなものは強まるばかりです。 そうして避けたまま、いざ避けられなくなったときに「基礎が身についていないため、典型的なミスをする」というのがありがちなパターンな気がします。
一生そうした感覚で生き続けることに耐えられなくなったため、2023 年頃にちゃんと勉強を始めました。 もっとも、浮動小数点型に限らず、そうしたブラックボックスめいたものの内部を知りたいがちな性格ではあったので、ようやくかという感じではあったかもしれません。
整数型との対比
これはぱっと思いついた例ですが、JPEG は「よくわからんがぐちゃぐちゃになっちゃうもの」という印象があり、PNG は「きれいなもの」という印象がありますよね。浮動小数点型と整数型の対比もそれと似ている気がします。
整数型はきっちりとした計算を行うことができるもので、たとえば 0x29AEDBEFB219581E18E1217B2715654F を 0x89FC309DF824DC7C で割ったあまり (%) などもやろうと思えば手計算で模倣することができると思われていそうです。たいへん面倒なのでやりたがる人はいないと思いますが、やり方自体がわからないということはないと思います。仮にお金がもらえるならやりますよね。
一方で、浮動小数点型においては、「コンピュータのお気持ち次第で誤差が出るような予測不能な変なもの」と思っている人がそれなりにいるのではないでしょうか。
たとえば 0x1.26ECE273B8E6Ap-11 と 0x1.EC8689D97BFB8p+3 の積 (*) を手計算で模倣することはできますか?
このへんは人によるかもで、「あくまで float -> float -> float のような関数の挙動が実質的には予測不能なだけで、値を固定すれば手計算(あるいは整数型の演算に帰着させるなどして)でも模倣可能」くらいに思っている人も多いのかもしれません? 多くの人がどう思っているのかもうわかりません。
とはいえ、整数型での演算でもブラックボックスなものはいくらでもあって、そうしたものについては「ほんとにどんなときでもこわれないの?」という気持ちは生じそうなので、浮動小数点型ばかりが除け者というわけではないのかもしれません。AtCoder Library の math にあるようなものたちを想像して話しています。
固定小数点型や十進浮動小数点型など
浮動小数点型はカスだが固定小数点型はカスではないとか、浮動小数点型の NaN はカスだとか、あれこれ理由をつけて(二進)浮動小数点型を貶めようとする言説はしばしば見かけます。
下記などが参考になります。
浮動小数点型はそんなに目の敵にされるべき発明ですか? むしろ概念自体は直感的まであるような気がします。 批判の要旨が「直感的・素朴すぎて、もっと洗練されるべきだ」のような感じなら納得しなくもないですが、もっと素朴で扱いにくいものが代替案として出されがちなので微妙な気持ちになります。
多倍長整数が定数時間で計算できると思って使っていそうな人々もいますし、自分の関心のない部分は「全部勝手にうまくいってくれる」と(無意識に?)思って代替案を出しているんだろうと思うと、理解はできます。
誤差
よくある 0.1 + 0.2 != 0.3 のような話題はもう見飽きましたが、「「$0.1$ を浮動小数点型で表せるように丸めたやつ」と「$0.2$ を浮動小数点型で表せるように丸めたやつ」の和を浮動小数点型で表せるように丸めたやつ」が「$0.3$ を浮動小数点型で表せるように丸めたやつ」と異なるという事実自体は、別にそこまで驚くべきことでもないのではないか?という気持ちはあります。
そもそも(オペランド自体の丸めもそうですが)+ 自体が単なる $+$ とは異なるものなので、「浮動小数点型では $0.1 + 0.2 = 0.3$ は成り立たない」のような言い方はちょっとうれしくないなぁと思ってしまいます。
よくある REPL などで「$0.1$ を浮動小数点型で表せるように丸めたやつ」が「0.1」と表示されることは非常に confusing で、変な誤解を生む要因の一つなんじゃないかなぁという思いはあります。下記みたいなのが人類にとって幸福かと言われると、それはそれで悩むところではあります。
>>> 0.1 0.1000000000000000055511151231257827021181583404541015625
これだと結局は「わけのわからないぐちゃぐちゃな値を $0.1$ に足したもの」だと認識するしかなく、「浮動小数点型は変なやつ」として避けられそうな気がします。 初心者に「実数を模倣できるやつ」だと誤解されて乱用されるよりはマシなのかもしれない気もします。
えびちゃんは、上記の値が $\tfrac1{10}(1+2^{-54})$ と正確に等しい値だと知ってから、それだけでお友だち度が少し増したような気持ちになりました。
比較
よく言われている「セオリー」の一つに、浮動小数点型の比較は x == y で行うのではなく、1e-15 などの十分小さい値を使って (x - y).abs() < 1e-15 のようにして行いましょうというものがあります。
やっぱりこれはどうにも怪しいという感想になってしまいます。1e-15 のような値が適切であるということはどうやって証明したのですか?という話です。
それが「なんとなく」なのであれば、怪しいのは浮動小数点型ではなく実装者です*1。
この小さい値の決め方(あるいはこうした比較の仕方が可能かどうか自体も)は状況次第であり、都度証明する必要がある*2ものです。 そもそも一般に、アルゴリズムの正当性というものに興味がない人ももしかして多いですか? 困ったものですね。
仮数部のビット長などの意味での精度 (precision) と、その型を用いて何らかの式を計算した際の精度 (accuracy) を区別せずに認識していて、「precision が 53 bits であれば、(ある程度複雑な式の)計算結果も $2^{-53} \approx 10^{-16}$ 程度くらいになるだろう」と思っている人もいるのではないでしょうか。さすがにそんなことはないですか?
数式での証明
IEEE 754 の規格によって、浮動小数点型の計算は(IEEE 754 に準拠したものであれば)環境によらずに再現性のあるものになっています。 この規格の登場前はプロセッサなどごとにさまざまな挙動をしていてカオスだったと聞いています。
再現性があるということは数式などによる証明も有効で、これは非常にうれしいことです。
にもかかわらず、やはりわざわざ「float を使うたびに数式(など)で証明を書く」という営みをしている人はそう多くはないような気がします。
なにをどう証明すればいいかもわからないし、そもそも証明できる(あるいは証明するべき)ことだと思っていない人が多数派なのかな?と思っています。
最近では AI によるコード生成が流行っていますが、(Gappa なり Coq なりを駆使しつつ)証明もやってくれたらうれしいものでしょうか。できるのかな? できないとはあまり思ってないですけど。
数学関数たちについて
数学関数たちの実装は必ずしも correctly-rounded になっていないことも多く、嫌な気持ちになります。LLVM は correct rounding な実装を 目指していそう なので、うれしい気持ちになります。がんばえーっっ
実務?
長めの保守を前提とする実務のコードで使いたいか?となってくると、これは話が別です。
リファクタの名目で括弧のつけ方を勝手に変えられるだけでめちゃくちゃになったり、そもそも証明を理解してメンテできる人の教育コストはたくさんだったり、単体テストでなにかを保証するのが大変だったり、本番環境でバグが出たときの再現に困ったりなど、ぱっと思いつくだけでも嫌な要素ばかりです。う〜〜ん参った。
でも現実はたぶんもっと酷くて、普通に桁落ちでめちゃくちゃになりうるようなコードが世の中の本番環境にたくさんデプロイされてたりするんじゃないですか?とも思ったりで、嫌〜〜という気持ちになります。
というわけなので、えびちゃんは浮動小数点型が好きではあるものの、「世の中の人がもっと使ってくれ〜〜」と思っているわけでもないです。 「世の中の人が浮動小数点型関連の証明をできるようになってくれ〜〜」とも別に思ってはいないですが、できるようになった世界も見てみたくはありますね。
浮動小数点型を使ったコードを書いているとレビュー時に反例を挙げて reject してくるえびちゃん概念、怖くて嫌ですね*3。あるいは「証明教えてください」とか言われても嫌だな。
所感
今までのも全部所感だろというのは正しいですね。メタ所感?
ネット上で見かけた怪しい言説や、浅い理解の解説ブログなどの印象に引っ張られすぎて、「大半の人類はまともに理解してないんじゃない?」と思っているだけかもしれません。書きながらそういう自覚は芽生えてきました。錯覚だったらうれしいです。
なにかしらを見るたびにもやもやするのに飽きたので、何度も思ったようなことをばーっと書き殴ったという感じです。
数値計算のエキスパートみたいな人自体はごろごろいるはずですから、えびちゃんもそういう人になりたいですね。 過去何度か挫折していますが、最近は数学関数の correct rounding な実装について調べています。 correct rounding な数学関数のライブラリに関しては、すでにメンテが終了したプロジェクトが複数あってかなしい気持ちです。 サーベイ論文 が 2025 年 2 月に更新されていたりして、まだまだ動きのある分野なのかな?と思ったりしています。 それなりに古くから(たとえば競プロ界隈ではあまり有名でないような)さまざまなことが研究されていそうなので、がんばらなきゃというところです。
「競プロ界隈で有名」ってなんだよという指摘はありますね。競プロ界隈で有名なものはセグ木と union-find しかないかもしれません。
おわり
明日からがんばります。