Rust Book 勉強会 #3 フォローアップ

Rust Book 勉強会で発表をしてきた。

rust-kansai.connpass.com

これは、結構前にQiitaのC++アドベントカレンダーで書いた内容と、Ben Deane氏によるCppConでの発表"Using types effectively"が下敷きになっている。

www.youtube.com

あ、この資料にクレジット書き忘れたのを思い出した、やっちまった

さて、その中で以下のRedditで出ていた話も紹介した。

www.reddit.com

スレ主の質問は、以下のenumのサイズが20になるのは何故かというものだ。

enum Vector {
    V2(f32,f32),
    V3(f32,f32,f32),
    V4(f32,f32,f32,f32),
}

答えは、データのレイアウトが以下のようになっているからだ。

|tag|padding|data region|
|u8 |[u8;3] |[u8;16]    |

データ領域は最大のものが格納できるサイズになり、どの値が入っているかを示すタグがきて、データ領域のアライメント調整用のパディングが入るというものだ。

さて、これは本当にこれ以上縮まないだろうか。

16バイトで表現できないのか?

できる気がしたので記事を書くことにする。ところでRustのf32とかf64ってIEEE754に従うよね?

NaNはご存知だろうか。Not A Numberの略で、値がもはや数値ではない場合に使う。例えば、0.0 / 0.0の結果などがNaNになる。このNaNは特殊な値で、それとの大抵の演算結果がNaNになり、またNaNに関するあらゆる比較は偽になる。NaN == NaNすら偽になる。

このNaNはexponentのビットが全て1で、mantissaが非ゼロという定義になっている。つまり、mantissaのほとんどは手付かずのまま残るということだ。mantissaのビットパターンを変えても、NaNのままにしておける。

そして、mantissaが割と自由に決められるということは、そこに情報を押し込めるということだ。これはNaN Boxingと言われており、言語処理系でたまに使われる。

つまりだ。Rustには、NaNの値として特別な値を準備しておき、先のenumV2に値が入っていたときに残りのf32tagged-NaNにしておいてV2であることを表現するという手段が取れるだろう。ではV4(1.0, 1.0, NaN, NaN)とどう区別するかというと、通常の演算で作られるNaNとは異なるビットパターンを持つNaNtagged-NaNとして使っておけば、区別できるのではないか。

|f32|f32|tagged-NaN|tagged-NaN|
|f32|f32|f32       |tagged-NaN|
|f32|f32|f32       |f32       |

まあ、これを実現するためにはアーキテクチャが通常の演算から返すNaNのビットパターンが固定されている必要と、NaNの演算結果でNaNのmantissaのビットパターンが変化しないことが保証されている必要がある。

どうなのだろう。そこのところをよく知らない。

とにかく、Rustは未だに上記のenumのサイズを20バイトにする。ここで書いたことが実はハードウェア的に実装不能なのか、それとも実装がややこしすぎる、あるいは手間に対して利益が少なすぎるとみなされているのか。私が気づくようなことがまだ提案されていないとはあまり思えないが、はて。