わからん
sil @signalingNaN : $@convention(method) (@thin Float.Type) -> Float {
bb0(%0 : $@thin Float.Type):
%1 = integer_literal $Builtin.Int32, 2141192192
%2 = builtin "bitcast_Int32_FPIEEE32"(%1 : $Builtin.Int32) : $Builtin.FPIEEE32
%3 = struct $Float (%2 : $Builtin.FPIEEE32)
return %3 : $Float
}
このsNaN (float) を返すSIL functionが
$ make float.ll
define swiftcc float @signalingNaN() #0 {
entry:
ret float 0x7FF4000000000000
}
こうなる。 定数部分がsNaN (double) になる。
x64向けにオブジェクトファイルを吐くと
$ make float.o
$ objdump -s float.o
...
Contents of section __literal4:
0010 0000a07f ....
...
定数がsNaN (float) に戻る。
wasm向けにオブジェクトファイルを吐くと
$ make float.wasm
$ wasm-objdump -d float.wasm
float.wasm: file format wasm 0x1
Code Disassembly:
00006c func[0] <signalingNaN>:
00006d: 01 7d | local[0] type=f32
00006f: 43 00 00 e0 7f | f32.const nan:0x600000
000074: 21 02 | local.set 2
000076: 20 02 | local.get 2
000078: 0f | return
000079: 0b | end
f32.constのオペランド部分が 0x7fe00000
、qNaN (float) になる。
- floatなのに64bit doubleに拡張されるのはなぜ?
- wasmの時だけqNaNに化けるのはなぜ?
ここでdoubleをfloatにキャストしてるのが問題。
int main(void) {
double d_snan = gen_double_snan();
dump_bits(&d_snan, sizeof(d_snan));
float casted = (float)d_snan;
dump_bits(&casted, sizeof(casted));
return 0;
}
これは以下を出力する。キャストするとqNaNビットが立つことがわかる。
0111111111110100000000000000000000000000000000000000000000000000
01111111111000000000000000000000
decimal | hex | binary | 備考 |
---|---|---|---|
2141192192 | 0x7fa00000 | 01111111101000000000000000000000 | sNaN (float) |
2145386496 | 0x7fe00000 | 01111111111000000000000000000000 | qNaN (float) |
9219994337134247936 | 0x7ff4000000000000 | 0111111111110100000000000000000000000000000000000000000000000000 | sNaN (double) |
9222246136947933184 | 0x7ffc000000000000 | 0111111111111100000000000000000000000000000000000000000000000000 | qNaN (double) |
CでsNaNを組み立てるコード書いたけど、これも同じような結果になったのでLLVMの問題っぽい