/*********************************************
 * 浮動小数点数加減算
 *********************************************/
#include <cstdlib>   // for rand()
#include <iostream>  // for cout
#include <math.h>    // for pow()
#include <stdio.h>   // for printf()
#include <time.h>    // for time()

#define D_MAX  11  // 有効桁数(小数点以下+べき指数)

using namespace std;

/*
 * 計算クラス
 */
class Calc
{
    int A[D_MAX];  // 被加減数配列
    int B[D_MAX];  // 加数配列
    int C[D_MAX];  // 減数配列

    public:
        Calc();                                    // コンストラクタ
        void calc();                               // 計算
    private:
        void add(int *, int *, int *, int, int);   // 加減算   ( 固定長整数 )
        void fadd(int *, int *, int *, int, int);  // 加減算   ( 浮動小数点数 )
        void setv(int, int*, int);                 // 値セット ( 全配列に同じ整数をセット )
        void copy(int *, int*, int);               // 値コピー ( 全配列をそのままコピー )
        void norm(int *, int);                     // 正規化   ( 固定長整数 )
        void fnorm(int *, int);                    // 正規化   ( 浮動小数点数 )
        void fdisp(int *, int);                    // 結果出力 ( 指数表示 )
};

/*
 * コンストラクタ
 */
Calc::Calc()
{
    // 使用する被加減数・加減数を設定(テストなので適当な乱数を使用)
    //   ( A + B, A - C で A > C となることが条件だが
    //     稀に条件に合致しない値が生成されるかもしれない )
    srand((unsigned int)time(NULL));                     // 乱数の種を初期化
    A[0] = rand() % (D_MAX - 1) + 1;                     // Aのべき指数部
    B[0] = rand() % (D_MAX - 1) + 1;                     // Bのべき指数部
    C[0] = A[0] - rand() % A[0] - 1;                     // Cのべき指数部
    for (int i = 1; i < D_MAX; i++) A[i] = rand() % 10;  // Aの小数部
    for (int i = 1; i < D_MAX; i++) B[i] = rand() % 10;  // Bの小数部
    for (int i = 1; i < D_MAX; i++) C[i] = rand() % 10;  // Bの小数部
}

/*
 * 計算
 */
void Calc::calc()
{
    // 各種宣言
    int size = D_MAX;  // 配列サイズ
    int a[size];       // 被加減数配列
    int b[size];       // 加数配列
    int c[size];       // 減数配列
    int z[size + 1];   // 計算結果配列

    // 初期設定
    copy(A, a, size);  // 被加減数配列
    copy(B, b, size);  // 加数配列
    copy(C, c, size);  // 減数配列

    // 計算する被加減数・加数・減数を出力
    printf("A =\n"); fdisp(a, size);
    printf("B =\n"); fdisp(b, size);
    printf("C =\n"); fdisp(c, size);

    // 加算 ( Z = A + B )
    fadd(a, b, z, size, 1);

    // 結果出力
    printf("A + B =\n"); fdisp(z, size);

    // 減算 ( Z = A - C )
    fadd(a, c, z, size, -1);

    // 結果出力
    printf("A - C =\n"); fdisp(z, size);
}

/*
 * 加減算
 * ( 固定長整数同士の加減算, 正規化含む )
 *
 *   a     [I ]  被加減数配列
 *   b     [I ]  加減数配列
 *   size  [I ]  配列数(A, B)
 *   z     [ O]  出力データ配列(Z = A + B or Z = A - B)
 *   id    [I ]  1: 加算(A + B(, -1: 減算(A - B)
 */
void Calc::add(int *a, int *b, int *z, int size, int id)
{
    int i;          // LOOPインデックス

    // 計算
    if (id >= 0) {  // 加算
        for (i = 0; i < size; i++)
            z[i] = a[i] + b[i];
    } else {        // 減算
        for (i = 0; i < size; i++)
            z[i] = a[i] - b[i];
    }

    // 正規化
    norm(z, size);
}

/*
 * 加減算
 * ( 浮動小数点数同士の加減算, 正規化含む )
 *
 *   a     [I ]  被加減数配列
 *   b     [I ]  加減数配列
 *   size  [I ]  配列数(A, B)
 *   z     [ O]  出力データ配列(Z = A + B or Z = A - B)
 *   id    [I ]  1: 加算(A + B), -1: 減算(A - B)
 */
void Calc::fadd(int *a, int *b, int *z, int size, int id)
{
    int k;  // べき指数の差
    int i;  // LOOPインデックス

    // 計算
    if (a[0] >= b[0]) {  // A のべき指数 >= B のべき指数
        k = a[0] - b[0];
        copy(a, z, k + 1);
        add(&a[k + 1], &b[1], &z[k + 1], size - k - 1, id);
    } else {             // A のべき指数 <  B のべき指数
        z[0] = b[0];
        k = b[0] - a[0];
        // 位の異なる部分
        if (id >= 0) {   // 加算
            copy(&b[1], &z[1], k);
        } else {         // 減算
            for (i = 1; i <= k; i++) z[i] = -b[i];
        }
        // 位の同じ部分の加減算
        add(&a[1], &b[k + 1], &z[k + 1], size - k - 1, id);
    }

    // 正規化
    norm(&z[1], size - 1);  // 固定長整数
    fnorm(z, size);         // 浮動小数点数
}

/*
 * 値セット
 * ( 全配列に同じ整数をセット )
 *
 *   v     [I  ]  セットする整数
 *   z     [  O]  セット先配列
 *   size  [I  ]  配列サイズ
 */
void Calc::setv(int v, int *z, int size) {
    int i;  // LOOPインデックス

    for (i = 0; i < size; i++) z[i] = v;
}

/*
 * 値コピー
 * ( 全配列をコピー )
 *
 *   a     [I  ]  コピー元配列
 *   z     [  O]  コピー先配列
 *   size  [I  ]  配列サイズ
 */
void Calc::copy(int *a, int *z, int size) {
    int i;  // LOOPインデックス

    for (i = 0; i < size; i++)
        z[i] = a[i];
}

/*
 * 正規化
 * ( Fixed Normalize(A) by Base-Value )
 *
 *   a     [IO]  正規化前データ・正規化後データ配列
 *   size  [I ]  配列数
 */
void Calc::norm(int *a, int size) {
    int i;       // LOOPインデックス
    int cr = 0;  // 繰り上がり

    for (i = size - 1; i >= 1; i--) {
        cr        = (int)(a[i] / 10);
        a[i]     -= cr * 10;
        a[i - 1] += cr;
    }

    for (i = size - 1; i >= 1; i--) {
        if (a[i] < 0) {
            a[i-1] -= 1;
            a[i]   += 10;
        }
    }
}

/*
 * 正規化
 * ( Floating Normalize(A) )
 *
 *   a     [IO]  正規化前データ・正規化後データ配列
 *   size  [I ]  配列数
 */
void Calc::fnorm(int *a, int size) {
    int exp;     // Exponent
    int i;       // LOOPインデックス
    int k;       // 上位の 0 の個数

    // 小数点以下第1位が桁あふれする場合、右に1桁シフト&べき指数加算
    if (a[1] >= 10) {
        for (i = size - 2; i >= 2; i--) a[i + 1] = a[i];
        a[2] = a[1] % 10;
        a[1] = a[1] / 10;
        a[0]++;
    }

    // 正規化前のべき指数を退避
    exp = a[0];

    // 上位の 0 の個数をカウント
    for (i = 1;  i <= size; i++) if (a[i] != 0) break;
    k = i - 1;

    // 上位の 0 の部分に以降の数字をシフト
    for (i = 1; i <= size - k; i++) a[i] = a[i + k];
    // シフト後の下位に 0 をセット
    setv(0, &a[size - k], k);

    // べき指数セット
    a[0] = exp - k;
}

/*
 * 結果出力 (指数表示)
 *
 *   s     [I ]  結果出力データ配列
 *   size  [I ]  配列数
 */
void Calc::fdisp(int *s, int size)
{
    int i;

    printf("0.");

    // 1行に50桁出力
    for (i = 1; i < size; i++) {
        printf("%d", s[i]);
        if (i % 10 == 0 && i != 0) printf(" ");
        if (i % 50 == 0 && i != 0) printf("\n  ");
    }

    // べき指数
    if (s[0] < 0) {
        printf(" * 10^(%d)\n", s[0]);
    } else {
        printf(" * 10^%d\n", s[0]);
    }
    printf("\n");
}

/*
 * メイン処理
 */
int main()
{
    try
    {
        // 計算クラスインスタンス化
        Calc objCalc;

        // 計算
        objCalc.calc();
    }
    catch (...) {
        cout << "例外発生!" << endl;
        return -1;
    }

    // 正常終了
    return 0;
}

By admin

Leave a Reply

Your email address will not be published.