0

任意精度の整数クラスを変換して、1桁あたり8ビットだけではない桁を使用できるようにしようとしています。奇妙な問題に遭遇しました。uint16_t 基本桁タイプには使用できますが、使用できませんuint32_t。私のコードは悪い結果を返します。何がうまくいかなかったのかを見つけるために使用した例は0x1111111111111111 * 0x1111111111111111、であるはずです0x123456789abcdf00fedcba987654321。しかし、私は得て0x123456789abcdf0fedcba987654321います。

基本桁のタイプを変更しても問題がないように、ハードコードされたすべてのタイプを変更したと思いましたが、明らかにそうではありません。

関連するコードは次のとおりです。

typedef uint32_t                          digit;            // original code uses uint8_t; uint16_t works too
typedef uint64_t                          double_digit;     // int type to hold overflow values

typedef std::deque <digit>                base;

const digit NEG1 = -1;                    // uint8_t -> 255, uint32_t -> 4294967295
const digit BITS = sizeof(digit) << 3;    // sizeof gives the number of bytes, so multiply that by 8 to get the number of bits
const digit HIGH_BIT = 1 << (BITS - 1);   // uint8_t -> 128

        // left bit shift. sign is maintained
        integer operator<<(uint64_t shift){
            if (!*this || !shift)
                return *this;
            base out = digits;
            for(uint64_t i = 0; i < (shift / BITS); i++)
                out.push_back(0);
            shift %= BITS;
            if (shift){
                out.push_back(0);
                return integer(out, _sign) >> (BITS - shift);
            }
            return integer(out, _sign);
        }

        // right bit shift. sign is maintained
        integer operator>>(uint64_t shift){
            if (shift >= bits())
                return integer(0);
            base out = digits;
            for(uint64_t i = 0; i < (shift / BITS); i++)
                out.pop_back();
            shift %= BITS;
            if (shift){
                base v;
                for(d_size i = out.size() - 1; i != 0; i--)
                    v.push_front(((out[i] >> shift) | (out[i - 1] << (BITS - shift))) & NEG1);
                v.push_front(out[0] >> shift);
                out = v;
            }
            return integer(out, _sign);
        }

        // operator+ calls this
        integer add(integer & lhs, integer & rhs){
            base out;
            base::reverse_iterator i = lhs.digits.rbegin(), j = rhs.digits.rbegin();
            bool carry = false;
            double_digit sum;
            for(; ((i != lhs.digits.rend()) && (j != rhs.digits.rend())); i++, j++){
                sum = *i + *j + carry;
                out.push_front(sum);
                carry = (sum > NEG1);
            }
            for(; i != lhs.digits.rend(); i++){
                sum = *i + carry;
                out.push_front(sum);
                carry = (sum > NEG1);
            }
            for(; j != rhs.digits.rend(); j++){
                sum = *j + carry;
                out.push_front(sum);
                carry = (sum > NEG1);
            }
            if (carry)
                out.push_front(1);
            return integer(out);
        }

        // operator* calls this
        // Long multiplication
        integer long_mult(integer & lhs, integer & rhs){
            unsigned int zeros = 0;
            integer row, out = 0;
            for(base::reverse_iterator i = lhs.digits.rbegin(); i != lhs.digits.rend(); i++){
                row.digits = base(zeros++, 0); // zeros on the right hand side
                digit carry = 0;
                for(base::reverse_iterator j = rhs.digits.rbegin(); j != rhs.digits.rend(); j++){
                    double_digit prod = (double_digit(*i) * double_digit(*j)) + carry;// multiply through
                    row.digits.push_front(prod & NEG1);
                    carry = prod >> BITS;
                }
                if (carry)
                    row.digits.push_front(carry);
                out = add(out, row);
            }
            return out;
        }

間違った計算を引き起こす可能性のある、私が見逃した明らかな何かがありますか?私はこのコードを一気に少し長すぎて見つめてきました。

完全に変更されたコードはここにあります。

編集:私はideoneでコードをテストしました、そしてそれはこの計算のために正しい値を返しています、しかし私のコンピュータはまだそうしません。これについて何か良い説明はありますか?

4

1 に答える 1

2

この問題は、意図しない過度の右シフトが原因であると思われます。あなたはあなたのideoneの投稿が問題を再現していないと言います。テスト中に、あなたのideoneの投稿で、数字のタイプが実際にはまだ16ビットに設定されていることに気付きました。これを(SOコードスニペットを反映するために)32ビットに変更し、gccでコンパイルしようとした瞬間、過度の右シフトに関する警告が多数表示されました。プログラムを実行すると無限ループが発生しました。これは、右シフトの警告と、プラットフォームで異なる動作が発生するという事実と相まって、未定義の動作が発生していることを強く示唆しています。

より具体的には、int型間でビットサイズの不一致が発生する場所があります(つまり、更新するコードのチャンクがまだいくつかあります)。

トラブルメーカーはsetFromZのようです。ここでは、Zで指定された整数型と、base::value_typeで指定された整数型があります。2つのビット数が同じであるとは限らないため、ビットの不一致が発生した場合、その関数のビットシフトコードとビットマスクコードは正しく機能しません。また、uint32_tが数字タイプの場合(少なくとも私の環境では)、ideoneコードで発生するそのテンプレートのいくつかのインスタンス化で不一致が発生しています。

編集:過度の右シフトが標準に従って定義されていないことの確認:参照:SO投稿、標準を引用している受け入れられた回答を参照してください。

(C++98§5.8/1)は次のように述べています。右のオペランドが負の場合、または プロモートされた左のオペランドのビット長以上の場合、動作は未定義です。

于 2012-10-23T20:12:02.993 に答える