float (単精度浮動小数点数)の内部表現

参考: 日経ソフトウエア 2020年1月号 [雑誌], pp.144-145

4バイトで表現される。今回実行した環境ではリトルエンディアンで格納されていた(下位バイトが若いアドレスに格納される)。

その4バイトの最上位ビットが符号(0: 正, 1: 負)、
続く8ビットが指数部(実際の指数に127を足した値)、
残りの23ビットが假数部(全24ビットのうち暗黙の最上位ビット1を外した値)。

#include <iostream>
#include <math.h>

using namespace std;

int main(void) {
    float a = -123.456;
    printf("a = %.20f\n", a);
    printf("floatは何バイトか: %lu\n", sizeof(float));

    // その4バイトを1個の32ビット整数に刻み直す:
    // &aで変数aのポインタを取得し、
    // (uint32_t*)でそのポインタをuint32_t型のポインタにキャストし、
    // *でそのポイント先に存在しているデータを取得する。
    uint32_t internal_representation = *(uint32_t*)&a;
    printf("内部表現は: 0x%X\n", internal_representation);

    // その4バイトを若いアドレスから1バイトずつ見てゆく:
    // &aで変数aのポインタを取得し、
    // (uint8_t*)でそのポインタをuint8_t型のポインタにキャストし、
    // 8ビット刻みで順番に参照する。
    printf("若いアドレスから1バイトずつ見てゆくと: ");
    for (int i = 0; i < 4; i++) {
        printf("0x%X, ", ((uint8_t*)&a)[i]);
    }
    putchar('\n');

    // 符号、指数部、假数部を個別に取り出す。
    uint8_t sign = (internal_representation >> 31) & 1;
    uint8_t exp_part = (internal_representation >> 23) & 0xFF;
    uint32_t mantissa = internal_representation & 0x7FFFFF;
    printf("符号: 0x%X\n", sign);
    printf("指数部: 0x%X\n", exp_part);
    printf("假数部: 0x%X\n", mantissa);

    // 元の数に戻してみる。
    mantissa += (1 << 23);
    exp_part -= 127;
    float sum = 0;
    for (int i = 0; i < 24; i++) {
        sum += ((mantissa >> (23 - i)) & 1) * pow(2, -i);
    }
    printf("元の数字に戻すと: ");
    printf("%.20f\n", pow((-1), sign) * sum * pow(2, exp_part));

    return 0;
}

f:id:ti-nspire:20201105072336p:plain