Last Updated 2004/02/10
Programming Tips Windows  索 引 
BCD演算
2004/02/10

固定小数点での演算を考える.
1桁1バイトが一番分かりやすい.(アンパックド10進)

struct BCD40 {
    char figure[40];
};

figure[] に代入するのは文字('0' 〜 '9')ではなく,数値(0x00 〜 0x09)とする.
このままでは表示できないので表示するには以下のようにする.


void PrintBCD40(LPTSTR lpszBuff, BCD40 *pTarget)
{
    int i;
    for(i = 0; i < 20; i++) {
        *lpszBuff++ = pTarget->figure[i] + _T('0');
    }
    *lpszBuff++ = _T('.');
    for(; i < 40; i++) {
        *lpszBuff++ = pTarget->figure[i] + _T('0');
    }
    *lpszBuff = _T('\0');
}

足し算

BOOL AddBCD40(BCD40 *pTarget, BCD40 *pVal)
{
    int i;
    BOOL bCarry = 0;
    for (i = 39; i >= 0; --i) {                         // 下の桁から計算
        pTarget->figure[i] += pVal->figure[i] + bCarry; // 加算分と桁上がり分を足す
        bCarry = pTarget->figure[i] >= 10;              // 桁上がりのチェック
        if (bCarry) {
            pTarget->figure[i] -= 10;                   // 桁上がり分を引く
        }
    }
    return bCarry;
}

引き算

BOOL SubBCD40(BCD40 *pTarget, BCD40 *pVal)
{
    int i;
    BOOL bBorrow = 0;
    for (i = 39; i >= 0; --i) {                          // 下の桁から計算
        pTarget->figure[i] -= pVal->figure[i] + bBorrow; // 減算分と借り入れ分を引く
        bBorrow = pTarget->figure[i] < 0;                // 借り入れのチェック
        if (bBorrow) {
            pTarget->figure[i] += 10;                    // 借り入れ分を足す
        }
    }
    return bBorrow;
}

掛け算

void MulBCD40(BCD40 *pTarget, BCD40 *pVal)
{
    BOOL aResult[80];               // 40桁*40桁は必ず80桁に収まる
    int i, j;
    int nCarry;
    for (i = 0; i < 80; i++) {
        aResult[i] = 0;             // まずは、バッファクリア
    }
    for (i = 39; i >= 0; --i) {
        nCarry = 0;
        for (j = 39; j >= 0; --j) {
            // 被乗数と乗数のある桁の積に、桁上がり分を加算
            aResult[i + 1 + j] += pTarget->figure[i] * pVal->figure[j] + nCarry;
            nCarry = aResult[i + 1 + j] / 10;      // 桁上がりのチェック
            if (nCarry) {
                aResult[i + 1 + j] -= nCarry * 10; // 引く
            }
        }
        aResult[i] = nCarry;                // 桁上がりを保存
    }
    for (i = 0; i < 40; i++) {
        pTarget->figure[i] = aResult[20 + i];   // 20.20 の部分のみコピー
    }
}

割り算

void DivBCD40(BCD40 *pTarget, BCD40 *pVal)
{
    char aWork[79];
    int i, nCount;
    for (i = 0; i < 79; i++) {
        aWork[i] = 0;                       // バッファクリア
    }
    for (i = 0; i < 40; i++) {
        aWork[19 + i] = pTarget->figure[i];     // 小数点を19シフト
    }
    for (i = 0; i < 40; i++) {
        nCount = 0;
        while (SubBCD40((BCD40 *)&aWork[i], pVal) == FALSE) {
            nCount++;                       // pVal で引ける分をカウント
        }
        AddBCD40((BCD40 *)&aWork[i], pVal); // 引きすぎたので戻す
        pTarget->figure[i] = nCount;
    }
}


符号が必要であれば構造体に符号をあらわすメンバ変数を追加する.
(割り算では注意が必要である)


参照
前後のTips
BCD演算

DSS ProgrammingTipsCGI Ver2.02