Last active
August 29, 2015 14:24
-
-
Save wakita/efc3ce97439c154384b4 to your computer and use it in GitHub Desktop.
単精度浮動小数点数の表現形式に関するハック
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#undef NDEBUG | |
#include <iostream> | |
#include <cassert> | |
using std::cout; | |
using std::endl; | |
// 1:8:23 | |
typedef unsigned int uint; | |
// 23: 7:16 | |
const uint EXP_MASK ((1 << 8) - 1); | |
const uint FRC_MASK ((1 << 23) - 1); | |
const uint B23 (1 << 23); | |
void p(float v) { | |
uint x = *reinterpret_cast<uint *>(&v); | |
uint s = x >> 31; | |
uint e = (x >> 23) & EXP_MASK; | |
uint f = x & FRC_MASK; | |
cout << | |
v << "\t-> " << (s ? '1' : '0') << ", " << | |
std::hex << e << ", " << | |
std::hex << f << std::dec << endl; | |
} | |
float fn1(int n) { | |
if (n == 0) return 0.f; | |
const uint s = 0; | |
uint e = 0x7f; | |
uint f = n, g = 0; | |
while (f > 1) { | |
g = ((f & 1) << (23 - 1)) | (g >> 1); | |
f >>= 1; e++; | |
} | |
uint x = (s << 31) | (e << 23) | g; | |
return *(reinterpret_cast<float *>(&x)); | |
} | |
float fn2(int n) { | |
if (n == 0) return 0.f; | |
const uint s = 0; | |
uint e = 0x7f + 23; | |
uint f = n; | |
while (!(f & B23)) { | |
f <<= 1; e--; | |
} | |
uint x = (s << 31) | (e << 23) | (f & FRC_MASK); | |
return *(reinterpret_cast<float *>(&x)); | |
} | |
float f01_1(int n) { | |
if (n == 0) return 0.f; | |
const uint s = 0; | |
uint e = 0x7f; | |
uint f = n; | |
while (!(f & B23)) { | |
f <<= 1; e--; | |
} | |
uint x = (s << 31) | (e << 23) | (f & FRC_MASK); | |
return *(reinterpret_cast<float *>(&x)); | |
} | |
int main() { | |
# define F 24 | |
for (int n = 0; n < (1 << F); n++) { | |
float f = (float)n / (1 << F); | |
assert (n == (uint)(f * (1 << F))); | |
} | |
cout << "floatの精度が" << F << "あることを確認しました。" << endl << endl; | |
// fn1: n -> (float)n の確認 | |
for (int n = 0; n < (1 << 23); n++) { | |
if (n <= 8 || (1 << 23) - 8 < n) { | |
p((float)n); | |
p(fn1(n)); | |
cout << endl; | |
} | |
assert(fn1(n) == (float)n); | |
} | |
cout << "fの実装の正しさを確認しました。" << endl << endl; | |
// fn1: n -> (float)n の確認 | |
cout << endl; | |
for (int n = 0; n < (1 << 23); n++) { | |
if (n <= 8 || (1 << 23) - 8 < n) { | |
p((float)n); | |
p(fn2(n)); | |
cout << endl; | |
} | |
assert(fn2(n) == (float)n); | |
} | |
cout << "fn2の実装の正しさを確認しました。" << endl << endl; | |
for (int n = 0; n < 23; n++) { | |
assert(f01_1(1 << n) * (1 << (23 - n)) == 1); | |
} | |
cout << "f01_1の実装の正しさを確認しました。" << endl << endl; | |
for (int n = 0; n < (1 << 23); n++) { | |
if (n <= 8 || (1 << 23) - 8 < n) { | |
p((float)n); | |
p(f01_1(n)); | |
cout << endl; | |
} | |
assert(f01_1(n) * (1 << 23) == n); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment