逆数を求めるトリッキー(?)なコード

ちょっと前に arton さんのところの「トリッキーなコード」というエントリーを受けて、id:kmt-t さんによる 反応 で引き合いにだされた、逆数を求める C++ のコード。

#include <stdio.h>
#define FP_ONE_BITS 0x3F800000

float p, r;
p = 1234.121234f;

int i = 2 * FP_ONE_BITS - *(int *)&p;
r = *(float *)&i;
r = r * (2.0f - p * r);

printf("inv  expected %20.10f  approx %20.10f\n", 1/p, r);
=> inv  expected         0.0008102932  approx        0.0008049050

これをその場では読み下せなかったのが、なんだか無性にくやしかったのを、最近、Eclipse に CDT を入れ直したときに思い出したので、もうちょっと C++ だのポインタだの浮動小数点数の内部表現だのを勉強してから SqueakSmalltalk への直訳を再度試みる。で、こんなんでました。

| fpOneBits result value ii |

fpOneBits := 16r3F800000.
value := 1234.121234 asFloat.

ii := 2 * fpOneBits - value asIEEE32BitWord.
result := Float fromIEEE32Bit: ii.
result := result * (2.0 - (value * result)).

^ 'inverse  expected = {1},  avaluerox = {2}' format: {1/value. result}
=> 'inverse  expected = 0.000810293164439629,  annrox = 0.000804904988199007'

なんでこれで逆数が求まるか、は、まあいいや(ぉぃ)。

1.0 asIEEE32BitWord storeStringBase: 16   " => '16r3F800000' "

というのがミソですかね。あとは、こんなとことか。

World findATranscript: nil.
#(0.125 0.25 0.5 1.0 2.0 4.0 8.0) do: [: each |
   | exp expStr |
   exp := each asIEEE32BitWord >> (32 - 8 - 1).
   expStr := exp printStringBase: 2 length: 8 padded: true.
   Transcript cr; show: each; tab; show: '->'; tab; show: expStr]
0.125 -> 01111100
0.25  -> 01111101
0.5   -> 01111110
1.0   -> 01111111
2.0   -> 10000000
4.0   -> 10000001
8.0   -> 10000010