1

浮動小数点演算に関する別のトピックです。

私はこれに頭を悩ませようとしてきましたが、負の浮動小数点値を追加してもうまくいかない理由がわかりません。

それらが両方とも正の値である場合、すべてが期待どおりに機能しています (これは結局のところ浮動小数点であるため、期待されたものから大きく外れていない数値を返します)。

明らかではない場合に備えて、32ビットバージョンを使用しています;)

私はすでにこれを読んでいます:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html そして、オンラインで浮動小数点演算のさまざまな素晴らしい説明を見つけました - しかし、私はまだ正と負の混合値 (または以下のコードのケース 2、3、および 4) を追加する方法を理解できないようです。

これまでの私のコードは次のとおりです。

int flt32_get_sign (flt32 x) {

    /** shift sign bit right; 0 = pos, 1 = neg */
    return ((x & 0x80000000) >> 31);
}

int flt32_get_exp (flt32 x) {

    /** get biaseed exponent value */
    return ((x & 0x7F800000) >> 23);
}

int flt32_get_val (flt32 x) {

    /** mask off mantissa 
    *   make sure implicit one set 
    */
    return ((x & 0x7FFFFF) ^ 0x800000);
}

int flt32_left_most_1 (int bits) {

    int position = -1;

    /** make sure working with abs value */
    if (flt32_get_sign(bits) != 0){
        bits = flt32_negate(bits);
    }

    while(bits != 0){
        position++, bits >>=1;
    }

    return position;
}

int flt32_right_most_1 (int bits) {

    int position = -1;

    /** make sure working with abs value */
    if (flt32_get_sign(bits) != 0){
        bits = flt32_negate(bits);
    }

    while (!(bits & 1)){
       position++, bits >>=1;
    }

    return position;
}

flt32 flt32_abs (flt32 x) {

    return (x & 0x7FFFFFFF);
}

flt32 flt32_negate (flt32 x) {

    if (flt32_get_sign(x) == 0){
        /** is possitive */
        return (x ^ 0x80000000);
    }
    /** else is negative */
    return (x & 0x7FFFFFFF);
}

flt32 flt32_add (flt32 x, flt32 y) {

    /** 
    *   Possible casses:
    *   1: +x +y; 2: +x -y; 3: -x +y; 4: -x -y
    */

    flt32 sum, x_val, y_val;
    int shift;


    /** Case 1 */
    if (flt32_get_sign(x) == 0 && flt32_get_sign(y) == 0){
        if (flt32_get_exp(x) == flt32_get_exp(y)){
            /** no shifting neccesary 
            *   add mantissa's then mask to make sure
            *   we don't get overflow into the exponent bits
            *   then add exponent back to new value
            */
            sum = (x & 0x7F800000) + ((flt32_get_val(x) + flt32_get_val(y)) & 0x7FFFFF);

        } else if (flt32_get_exp(x) > flt32_get_exp(y)){
            /** exponent of x is larger than y
            *   need to right shift y and set its exponent = exponent of x
            */
            shift = (flt32_get_exp(x) - flt32_get_exp(y));
            y_val = flt32_get_exp(x) + (flt32_get_val(y) >> shift);

            sum = x + y_val;

        } else {
            /** exponent x is smaller than y
            *   need to right shift x and set its exponent = exponent of y 
            */
            shift = (flt32_get_exp(y) - flt32_get_exp(x));
            x_val = flt32_get_exp(y) + (flt32_get_val(x) >> shift);

            sum = x_val + y;
        }
    }

    /** Case 2 */
    if (flt32_get_sign(x) == 0 && flt32_get_sign(y) == 1){
        if (flt32_get_exp(x) == flt32_get_exp(y)){
            /** no shifting neccesary 
            *   add mantissa's then mask to make sure
            *   we don't get overflow into the exponent bits
            *   then add exponent back to new value
            */
            x_val = ((x & 0xFF800000) + flt32_get_val(x));
            y_val = ((y & 0xFF800000) + flt32_get_val(y));

            sum = x_val + flt32_negate(y_val);

        } else if (flt32_get_exp(x) > flt32_get_exp(y)){
            /** exponent of x is larger than y
            *   need to right shift y and set its exponent = exponent of x
            */
            shift = (flt32_get_exp(x) - flt32_get_exp(y));
            y_val = flt32_get_exp(x) + (flt32_get_val(y) >> shift);

            sum = x + flt32_negate(y_val);

        } else {
            /** exponent x is smaller than y
            *   need to right shift x and set its exponent = exponent of y 
            */
            shift = (flt32_get_exp(y) - flt32_get_exp(x));
            x_val = flt32_get_exp(y) + (flt32_get_val(x) >> shift);

            sum = x_val + flt32_negate(y);
        }
    }

    return sum;
}

余談ですが、これらすべてに頭を悩ませているときに私が行った観察にすぎません。浮動小数点を理解することは素晴らしいことであり、必要でさえあるように思えますが、私が遭遇したほぼすべての記事は、教科書でさえも、可能な限りそれを避けるように言っています! :)

4

1 に答える 1

2

ケース 2 の考慮事項

sum = x + flt32_negate(y_val)、 shouldの後 、sum < 0x800000答えを正規化する必要があります。そうでない場合は左シフトし、進むにつれて指数を減らします。最初に監視し、sum == 0特別に形成された 0 を返します。一度修正すると、バイアス指数をデクリメントするときにアンダーフローをチェックしませんflt32_get_val()

さらなる違いの問題: 減算 (+ ケース 2) はshift、問題のために、一部としてシフトアウトされたビットが戻されるように実行する必要がありますsum < 0x800000。シフトアウトされたビットが残っている場合は、丸めを評価して処理する必要があります。

+ と - を一般的なルーチンで処理し、マグニチュードの加算/減算にベクトル化することを検討してください。+ ケース 1,2,3,4 は - ケース 1,4,3,2 に似ています。+ ケース 1,4 は同じで、符号が異なるだけです。+ ケース 2,3 は、最初にどちらが大きいかを見つけたら、互いの否定です。

最初は +0、-0 を特殊なケースとして扱うことを検討してください。

INF と NAN については何もしていません。後で保存することをお勧めします。今はスタブに入れます。


ケース 1 の考慮事項

の場合sum > 0xFFFFFFは、指数を右に移動してインクリメントし、指数のオーバーフローと返される INF をテストする必要があります。

右シフトの場合、OP は丸めモードを決定し、場合によっては回答をインクリメントする必要があります。インクリメント指数を再度テストし、指数オーバーフローをテストし、INF を返します。


作成後sumの回答再構成は、隠し MSBit の扱いに注意する必要があります。

OPは|の使用を混同しています 対^。

OP にはまだ長い道のりがあります。OP はケースの追加で 10% 完了していると思います。

于 2013-09-26T03:20:30.210 に答える