やってしまいました。
ここに vec2
の変数があります。
vec2 v;
各要素の精度を 16 ビットまで落として、一つの 32 ビット整数に pack します。
uint a = packHalf2x16(v);
そのままでは出力できなかったので、同じく 32 ビットの浮動小数点数である float
に変換して書き出します。
float output = uintBitsToFloat(a);
次読んだときにはデータは消えていました。
32 ビット浮動小数点数は 2^{32}
通りの値を表現できない
以降 IEEE 754 の規格に沿った浮動小数点数だけを考えます。
32 ビットの浮動小数点数には、厳密には 32 ビット分の分解能はありません。
非正規化数
指数部が 0 で、仮数部が 0 でない浮動小数点数は非正規化数と呼ばれます。0 ではないけれども絶対値が小さすぎて \pm2^{\text{exp}}\times1.\cdots
の形で表せない数のことです。
ハードウェアによっては、とりわけ GPU の場合は非正規化数の正確な処理に対応しておらず、非正規化数を切り捨ててゼロにしてしまう(アンダーフロー)ことがあります。
自分の場合はこれに引っかかっており、仮数部のデータが吹き飛んでゼロになってしまっていました。
無限大
指数部が最大で、仮数部が 0 のときは符号部のビットに対応する符号の無限大を表します。この対応は全単射なので特に恐れることはありません。
NaN
指数部が最大で、仮数部が 0 でないときは非数 (NaN, Not a Number) を表します。非正規化数にハードウェアが対応していなかったときと同じく、仮数部のデータを握り潰して同じ値に落とし込んでしまいます。
整数型を使おう
面倒でも整数を保存する際にはきちんと整数型の保存場所を確保しておきましょう。
WebGL 2.0 ではテクスチャの内部フォーマットとして RGBA32I
や RGBA32UI
が使えます。うまく活用してよい GPGPU ライフを送りましょう。