非専門的シンギュラリティー研究所

無限に動き続けるシステムを表す方法を AI なども使って考えていきます。

昔ながらの BASIC(9)

16ビット整数の乗算・除算

Z80 では乗算・除算の命令がないため、処理を書く必要があります。この処理を C++ で説明します。16ビット整数の乗算・除算を C++ で書くと以下のようになります。レジスターを節約するため引数と結果をまとめて 16 ビットで表しやすい方法考えます。そのため複数の方法を考えます。

乗算

以下の multiply1 または multiply2 のようになります。bitsize は 16、x、y は 16 ビットの符号なし整数、int は 32 ビット以上あるとします。

int multiply1(int x, int y, int bitsize) {
    int result = 0;
    for (int i = 0; i < bitsize; i++) {
        if (y & 1) {
            result += x;
        }
        x <<= 1;
        y >>= 1;
    }
    return result;
}
int multiply2(int x, int y, int bitsize) {
    int c = 1 << bitsize;
    int result = 0;
    for (int i = 0; i < bitsize; i++) {
        y <<= 1;
        result <<= 1;
        if (y & c) {
            result += x;
        }
    }
    return result;
}
除算

以下の divide1 または divide2 のようになります。bitsize は 16、x、y は 16 ビットの符号なし整数、int は 32 ビット以上あるとします。

int divide1(int x, int y, int bitsize) {
    int result = 0;
    y <<= bitsize;
    for (int i = 0; i < bitsize; i++) {
        y >>= 1;
        result <<= 1;
        if (x >= y) {
            result++;
            x -= y;
        }
    }
    return result;
}
int divide2(int x, int y, int bitsize) {
    int result = 0;
    for (int i = 0; i < bitsize; i++) {
        x <<= 1;
        result <<= 1;
        if (x >= y << bitsize) {
            result++;
            x -= y << bitsize;
        }
    }
    return result;
}

16ビット整数の乗算・除算の説明

説明のため assert を入れたものを掲載します。multiply2 の assert が間違っていることがわかったので、Visual StudioGitHub Copilot を使って「multiply2 の assert を正しく書き直してください」と入力したら作ってくれました。assert の式が複雑なのでほかの assert も間違っている可能性がありますが、今のところ説明のためには問題ないようなのでこのまま続けます。

乗算
/// <summary>
/// 指定したビット数の乗算
/// </summary>
/// <param name="x">被乗数</param>
/// <param name="y">乗数</param>
/// <param name="bitsize">被乗数・乗数のビット数(int のビット数の半分以下)</param>
/// <returns>積</returns>
int multiply1(int x, int y, int bitsize) {
    assert(x >= 0 && x < 1 << 16);
    assert(y >= 0 && y < 1 << 16);
    assert(bitsize <= 16);
    // x の初期値を x0、y の初期値を y0 とする
    int x0 = x;
    int y0 = y;
    int result = 0;
    for (int i = 0; i < bitsize; i++) {
        // x = x0 << i
        assert(x == x0 << i);
        // y = y0 >> i
        assert(y == y0 >> i);
        // result = x0 * (y0 の 下位 i ビット)
        assert(result == x0 * (y0 & ((1 << i) - 1)));
        if (y & 1) {
            result += x;
        }
        x <<= 1;
        y >>= 1;
        // x = x0 << (i + 1)
        assert(x == x0 << (i + 1));
        // y = y0 >> (i + 1)
        assert(y == y0 >> (i + 1));
        // result = x0 * (y0 の 下位 i + 1 ビット)
        assert(result == x0 * (y0 & ((1 << (i + 1)) - 1)));
    }
    // result = x0 * y0
    assert(result == x0 * y0);
    return result;
}
/// <summary>
/// 指定したビット数の乗算
/// </summary>
/// <param name="x">被乗数</param>
/// <param name="y">乗数</param>
/// <param name="bitsize">被乗数・乗数のビット数(int のビット数の半分以下)</param>
/// <returns>積</returns>
int multiply2(int x, int y, int bitsize) {
    assert(x >= 0 && x < 1 << 16);
    assert(y >= 0 && y < 1 << 16);
    assert(bitsize <= 16);
    // x の初期値を x0、y の初期値を y0 とする
    int x0 = x;
    int y0 = y;
    int c = 1 << bitsize;
    int result = 0;
    for (int i = 0; i < bitsize; i++) {
        // x = x0
        assert(x == x0);
        // y = y0 << i
        assert(y == y0 << i);
        // result = x0 * (y0 の 上位 i ビットを右シフトして上位 i ビットだけにしたもの)
        assert(result == x0 * ((y0 >> (bitsize - i)) & ((1 << i) - 1)));
        y <<= 1;
        result <<= 1;
        if (y & c) {
            result += x;
        }
        // x = x0
        assert(x == x0);
        // y = y0 << (i + 1)
        assert(y == y0 << (i + 1));
        // result = x0 * (y0 の 上位 i + 1 ビットを右シフトして上位 i + 1 ビットだけにしたもの)
        assert(result == x0 * ((y0 >> (bitsize - (i + 1))) & ((1 << (i + 1)) - 1)));
    }
    // result = x0 * y0
    assert(result == x0 * y0);
    return result;
}
除算
/// <summary>
/// 指定したビット数の除算
/// </summary>
/// <param name="x">被除数</param>
/// <param name="y">除数</param>
/// <param name="bitsize">被除数・除数のビット数(int のビット数の半分以下)</param>
/// <returns>商</returns>
int divide1(int x, int y, int bitsize) {
    assert(x >= 0 && x < 1 << 16);
    assert(y >= 0 && y < 1 << 16);
    assert(bitsize <= 16);
    // x の初期値を x0、y の初期値を y0 とする
    int x0 = x;
    int y0 = y;
    int result = 0;
    y <<= bitsize;
    for (int i = 0; i < bitsize; i++) {
        // y = y0 << (bitsize - i)
        assert(y == y0 << (bitsize - i));
        // result = x0 を y0 で割った商の 上位 i ビットを右シフトして上位 i ビットだけにしたもの)
        assert(result == ((x0 / y0) & ((1 << i) - 1)) >> (bitsize - i));
        // x = x0 - y0 * (x0 を y0 で割った商の 上位 i ビット)
        assert(x == x0 - y0 * (((x0 / y0) & ((1 << i) - 1) << (bitsize - i))));
        y >>= 1;
        result <<= 1;
        if (x >= y) {
            result++;
            x -= y;
        }
        // y = y0 << (bitsize - (i + 1))
        assert(y == y0 << (bitsize - (i + 1)));
        // result = x0 を y0 で割った商の 上位 i + 1 ビットを右シフトして上位 i + 1 ビットだけにしたもの)
        assert(result == ((x0 / y0) & ((1 << (i + 1)) - 1)) >> (bitsize - (i + 1)));
        // x = x0 - y0 * (x0 を y0 で割った商の 上位 i + 1 ビット)
        assert(x == x0 - y0 * (((x0 / y0) & ((1 << (i + 1)) - 1) << (bitsize - (i + 1)))));
    }
    // result = x0 を y0 で割った商
    assert(result == x0 / y0);
    // x = x0 を y0 で割った余り
    assert(x == x0 % y0);
    return result;
}
/// <summary>
/// 指定したビット数の除算
/// </summary>
/// <param name="x">被除数</param>
/// <param name="y">除数</param>
/// <param name="bitsize">被除数・除数のビット数(int のビット数の半分以下)</param>
/// <returns>商</returns>
int divide2(int x, int y, int bitsize) {
    assert(x >= 0 && x < 1 << 16);
    assert(y >= 0 && y < 1 << 16);
    assert(bitsize <= 16);
    // x の初期値を x0、y の初期値を y0 とする
    int x0 = x;
    int y0 = y;
    int result = 0;
    for (int i = 0; i < bitsize; i++) {
        // y = y0
        assert(y == y0);
        // result = x0 を y0 で割った商の 上位 i ビットを右シフトして上位 i ビットだけにしたもの)
        assert(result == ((x0 / y0) & ((1 << i) - 1)) >> (bitsize - i));
        // x = (x0 - y0 * (x0 を y0 で割った商の 上位 i ビット)) を i ビット左シフトしたもの
        int q = (x0 / y0) & (((1 << i) - 1)) << (bitsize - i);
        assert(x == (x0 - y0 * q) << i);
        x <<= 1;
        result <<= 1;
        if (x >= y << bitsize) {
            result++;
            x -= y << bitsize;
        }
        // y = y0
        assert(y == y0);
        // result = x0 を y0 で割った商の 上位 i + 1 ビットを右シフトして上位 i + 1 ビットだけにしたもの)
        assert(result == ((x0 / y0) & ((1 << (i + 1)) - 1)) >> (bitsize - (i + 1)));
        // x = (x0 - y0 * (x0 を y0 で割った商の 上位 i + 1 ビット)) を i + 1 ビット左シフトしたもの
        int qq = (x0 / y0) & (((1 << (i + 1)) - 1)) << (bitsize - (i + 1));
        assert(x == (x0 - y0 * qq) << (i + 1));
    }
    // result = x0 を y0 で割った商
    assert(result == x0 / y0);
    // x = (x0 を y0 で割った余り) << bitsize
    assert(x == (x0 % y0) << bitsize);
    return result;
}