exprでオーバーフローを回避する方法。あいうえお


161

:私はのように見える表現を計算する必要があり A*B - C*D、その種類は次のとおりです。signed long long int A, B, C, D; それぞれの数字は(その型をオーバーフローしない)本当に大きなことができます。一方でA*B、オーバーフローを引き起こす可能性があり、同時に式はA*B - C*D本当に小さくすることができます。どうすれば正しく計算できますか?

例:MAX * MAX - (MAX - 1) * (MAX + 1) == 1、ここでMAX = LLONG_MAX - n、n-ある自然数。


17
精度はどのくらい重要ですか?
Anirudh Ramanathan

1
@クトゥルフ、素晴らしい質問。彼は、それらすべてを10で割ったり、結果を掛けたりすることで、より小さい数を使用して同等の関数を作成しようとする可能性があります。
Chris、

4
Vars A、B、C、Dは署名されています。これA - Cは、オーバーフローする可能性があることを意味します。考慮すべき問題、またはこれがデータで発生しないことを知っていますか?
ウィリアムモリス

2
@MooingDuckですが、操作がオーバーフローするかどうか事前に確認できますstackoverflow.com/a/3224630/158285
bradgonesurfing

1
@クリス:いいえ、署名されたオーバーフローが発生したかどうかを確認するポータブルな方法はないと言っています。(ブラッドは、それ起こることをポータブルに検出できることは正しいです)。インラインアセンブリの使用は、多くの移植性のないチェック方法の1つです。
Mooing Duck

回答:


120

これはささいなことだと思います。しかしA*B、オーバーフローする可能性があるものです。

精度を失うことなく、次のことができます

A*B - C*D = A(D+E) - (A+F)D
          = AD + AE - AD - DF
          = AE - DF
             ^smaller quantities E & F

E = B - D (hence, far smaller than B)
F = C - A (hence, far smaller than C)

この分解はさらに行うことができます。
@Gianが指摘したように、型がunsigned long longである場合、減算演算中に注意が必要になる場合があります。


たとえば、あなたが問題に持っている場合、それはたった1回の反復を必要とします、

 MAX * MAX - (MAX - 1) * (MAX + 1)
  A     B       C           D

E = B - D = -1
F = C - A = -1

AE - DF = {MAX * -1} - {(MAX + 1) * -1} = -MAX + MAX + 1 = 1

4
@Caleb、同じアルゴリズムを適用するC*D
Chris

2
Eが何を表しているのか説明してください。
カレブ

7
long longとdoubleはどちらも64ビットです。doubleは指数にいくつかのビットを割り当てる必要があるため、精度を失うことなく、可能な値の範囲が小さくなります。
ジムギャリソン

3
@Cthulhu-これはすべての数が非常に大きい場合にのみ機能するように思えます...たとえば、まだ{A、B、C、D} = {MAX、MAX、MAX、2}でオーバーフローが発生します。OPは「各数値は非常に大きくなる可能性がある」と述べていますが、問題の説明から、各数値が非常に大きい必要があることは明らかではありません。
Kevin K

4
A,B,C,Dしかし、いずれかが否定的である場合はどうなりますか?それではもっと大きくならないEF
2012年

68

最も簡単で最も一般的な解決策は、長整数ライブラリ(http://gmplib.org/など)を使用するか、構造体または配列を使用して表現し、一種の長い乗算を実装することにより、オーバーフローできない表現を使用することです(つまり、各数値を2つの32ビット半分に分離し、以下のように乗算を実行します。

(R1 + R2 * 2^32 + R3 * 2^64 + R4 * 2^96) = R = A*B = (A1 + A2 * 2^32) * (B1 + B2 * 2^32) 
R1 = (A1*B1) % 2^32
R2 = ((A1*B1) / 2^32 + (A1*B2) % 2^32 + (A2*B1) % 2^32) % 2^32
R3 = (((A1*B1) / 2^32 + (A1*B2) % 2^32 + (A2*B1) % 2^32) / 2^32 + (A1*B2) / 2^32 + (A2*B1) / 2^32 + (A2*B2) % 2^32) %2^32
R4 = ((((A1*B1) / 2^32 + (A1*B2) % 2^32 + (A2*B1) % 2^32) / 2^32 + (A1*B2) / 2^32 + (A2*B1) / 2^32 + (A2*B2) % 2^32) / 2^32) + (A2*B2) / 2^32

最終結果が64ビットに収まると仮定すると、実際にはR3のほとんどのビットは不要で、R4は不要です。


8
上記の計算は見た目ほど複雑ではありません。2^ 32での単純な長い乗算であり、Cのコードはより単純に見えるはずです。また、プログラムでこの作業を行うには、汎用関数を作成することをお勧めします。
Ofir

46

これはラップアラウンドの署名済みオーバーフローに依存しているため、標準ではないことに注意してください。(GCCにはこれを可能にするコンパイラフラグがあります。)

ただし、ですべての計算を行うだけの場合long long、式を直接適用した結果は
(A * B - C * D)、正しい結果がに収まる限り正確long longです。


これは、符号なし整数を符号付き整数にキャストする実装定義の動作のみに依存する回避策です。しかし、これは今日のほとんどすべてのシステムで機能すると予想できます。

(long long)((unsigned long long)A * B - (unsigned long long)C * D)

これによりunsigned long long、オーバーフロー動作が標準でラップアラウンドされることが保証されている場所に入力がキャストされます。最後に符号付き整数にキャストバックすることは実装定義部分ですが、今日のほぼすべての環境で機能します。


より詳細なソリューションが必要な場合は、「長い演算」を使用する必要があると思います


+1これに気づくのはあなただけです。唯一のトリッキーな部分は、ラップアラウンドオーバーフローを実行するようにコンパイラーを設定し、正しい結果が実際にに収まるかどうかをチェックすることですlong long
Mysticial

2
まったくトリックのない単純なバージョンでさえ、ほとんどの実装で正しく動作します。規格では保証されていませんが、1の補数のマシンまたはその他の非常に奇妙なデバイスを見つけて失敗させる必要があります。
ホッブズ

1
これは重要な答えだと思います。実装固有の動作を想定するのは正しいプログラミングではない可能性があることには同意しますが、パフォーマンスが重要な場合は、すべてのエンジニアがモジュロ演算と正しいコンパイラフラグを取得して一貫した動作を確保する方法を理解する必要があります。DSPエンジニアは、固定小数点フィルターの実装についてこの動作に依存しています。そのため、受け入れられた答えは受け入れられないパフォーマンスになります。
Peter M

18

これはうまくいくはずです(私は思う):

signed long long int a = 0x7ffffffffffffffd;
signed long long int b = 0x7ffffffffffffffd;
signed long long int c = 0x7ffffffffffffffc;
signed long long int d = 0x7ffffffffffffffe;
signed long long int bd = b / d;
signed long long int bdmod = b % d;
signed long long int ca = c / a;
signed long long int camod = c % a;
signed long long int x = (bd - ca) * a * d - (camod * d - bdmod * a);

これが私の派生です:

x = a * b - c * d
x / (a * d) = (a * b - c * d) / (a * d)
x / (a * d) = b / d - c / a

now, the integer/mod stuff:
x / (a * d) = (b / d + ( b % d ) / d) - (c / a + ( c % a ) / a )
x / (a * d) = (b / d - c / a) - ( ( c % a ) / a - ( b % d ) / d)
x = (b / d - c / a) * a * d - ( ( c % a ) * d - ( b % d ) * a)

1
@bradgonesurfingに感謝します-そのような情報を提供していただけますか?私は私の答えを更新し、それを実行し、それが機能します(bdとcaは0)...
paquetp

1
うーん。今はそうではないかもしれません。d = 1とa = 1とb = maxintとc = maxintを使用して縮退したケースでも機能します。クール:)
bradgonesurfing

1
@paquetp:a = 1、b = 0x7fffffffffffffff、c = -0x7fffffffffffffff、d = 1(cは負であることに注意)。賢いですが、あなたのコードがすべての正の数を正しく処理すると確信しています。
Mooing Duck

3
@MooingDuckですが、セットの最終的な回答もオーバーフローしているため、有効なセットアップではありません。各側が同じ符号である場合にのみ機能し、結果の減算は範囲内になります。
Bradgonesurfing

1
StackOverflowに奇妙な点があります。この答えが最もシンプルで最良の場合、トップスコアの回答と比較してスコアが非常に低くなります。
bradgonesurfing

9

すべての値について最大の共通因子を計算し、算術演算を実行する前にそれらをその因子で除算してから、再度乗算することを検討できます。しかし、これは、このような要因が存在することを前提として(例えば、もしABCD彼らは共通因子を持っていないだろう、互いに素ことが起こります)。

同様に、対数スケールでの作業を検討することもできますが、これは数値の精度に応じて少し恐ろしいことになります。


1
long double利用可能であれば対数は良いようです。その場合、許容レベルの精度を達成できます(結果は丸められます)。

9

結果がlong long intに収まる場合、式A * BC * Dは算術mod 2 ^ 64を実行し、正しい結果を与えるので問題ありません。問題は、結果がlong long intに収まるかどうかを知ることです。これを検出するには、doubleを使用して次のトリックを使用できます。

if( abs( (double)A*B - (double)C*D ) > MAX_LLONG ) 
    Overflow
else 
    return A*B-C*D;

このアプローチの問題は、倍精度の仮数の精度(54ビット?)によって制限されるため、A * BおよびC * Dの積を63 + 54ビット(またはおそらく少し少ない)に制限する必要があることです。


これは最も実用的な例です。クリアして正しい答えを出します(または、入力が正しくない場合は例外をスローします)。
Mark Lakata

1
素敵でエレガント!あなたは他の人が陥った罠に落ちませんでした。もう1つだけ:丸め誤差のために、二重計算がMAX_LLONGを下回る例がいくつかあると思います。私の数学的な本能は、代わりにdoubleとlongの結果の差を計算し、それをMAX_LLONG / 2または何かと比較する必要があることを教えてくれます。この違いは、double計算の丸め誤差とオーバーフローであり、通常は比較的低いはずですが、前述の場合は大きくなります。しかし、今のところ私は怠惰すぎて確実に知ることができません。:-)
Hans-PeterStörr12年

9
E = max(A,B,C,D)
A1 = A -E;
B1 = B -E;
C1 = C -E;
D1 = D -E;

その後

A*B - C*D = (A1+E)*(B1+E)-(C1+E)(D1+E) = (A1+B1-C1-D1)*E + A1*B1 -C1*D1

7

各数値を配列に書き込み、各要素を数字にして、計算を多項式として行うことができます。結果の多項式(配列)を取得し、配列の各要素に配列の位置(最初の位置が最大で最後がゼロ)の累乗で10を掛けて結果を計算します。

123は次のように表すことができます。

123 = 100 * 1 + 10 * 2 + 3

配列を作成するだけ[1 2 3]です。

これをすべての数値A、B、C、Dに対して実行し、それらを多項式として乗算します。結果の多項式を取得したら、それから数を再構築するだけです。


2
それが何であるかわからないが、私は見つける必要があります。置く:)。これは、ガールフレンドと買い物をしている間の頭のてっぺんの解決策です:)
Mihai

あなたはbase10配列にbignumを実装しています。GMPは、ベース4294967296を使用する高品質のbignumライブラリです。はるかに高速です。正解で便利なので、反対投票はありません。
Mooing Duck

おかげで:)。これを行う方法の1つであることを知っておくと便利ですが、もっと良い方法があるので、このようにしないでください。少なくともこの状況ではありません:)
Mihai

とにかく...このソリューションを使用すると、どのプリミティブ型も太字(100桁の数値など)よりもはるかに大きな数をコンピューターで処理し、結果を配列として保持できます。これは賛成票に値します:p
Mihai

この方法(効果的で比較的簡単に理解できます)はメモリを大量に消費し、処理速度が遅いため、賛成票が得られるかどうかはわかりません。
Mooing Duck

6

一方でsigned long long int保持していないだろうA*B、彼ら二人はなります。したがってA*B、異なる指数のツリー項に分解でき、それらのいずれも1に適合しsigned long long intます。

A1=A>>32;
A0=A & 0xffffffff;
B1=B>>32;
B0=B & 0xffffffff;

AB_0=A0*B0;
AB_1=A0*B1+A1*B0;
AB_2=A1*B1;

も同じですC*D

まっすぐ進むと、サブキャリーはペアごとにAB_iCD_i同様に、それぞれに追加のキャリービット(正確には1ビット整数)を使用して実行できます。したがって、E = A * BC * Dとすると、次のようになります。

E_00=AB_0-CD_0 
E_01=(AB_0 > CD_0) == (AB_0 - CD_0 < 0) ? 0 : 1  // carry bit if overflow
E_10=AB_1-CD_1 
...

E_10toの上半分を転送して続行しますE_20(32だけシフトして追加し、次にの上半分を消去しE_10ます)。

これで、キャリービットE_11を正しい記号(キャリー以外の部分から取得)でに追加することで、キャリービットを取り除くことができますE_20。これによりオーバーフローがトリガーされると、結果も適合しません。

E_10E_00 (シフト、追加、消去)とキャリービットから上半分を取得するのに十分な「スペース」が追加されましたE_01

E_10再び大きくなる可能性があるため、への転送を繰り返しE_20ます。

この時点で、E_20はゼロになる必要があります。そうでない場合、結果は適合しません。E_10転送の結果、上半分も空になります。

最後のステップは、下半分E_20E_10再び移動することです。

ホールドにE=A*B+C*D合うと予想される場合signed long long int、私たちは今持っています

E_20=0
E_10=0
E_00=E

1
これは実際には、Ofirの乗算式を使用して不要な一時結果をすべて削除した場合に得られる簡略化された式です。
dronus

3

最終結果が整数型で表現できることがわかっている場合は、以下のコードを使用してこの計算をすばやく実行できます。C規格では、符号なし演算はモジュロ演算でオーバーフローしないと規定されているため、符号なし型を使用して計算を実行できます。

次のコードは、同じ幅の符号なし型があり、符号付き型がすべてのビットパターンを使用して値を表すことを前提としています(トラップ表現なし、符号付き型の最小値は符号なし型の係数の半分の負です)。これがCの実装に当てはまらない場合は、そのためにConvertToSignedルーチンに簡単な調整を加えることができます。

次のコードはsigned char、とunsigned charを使用して示しています。あなたの実装では、定義の変更Signedtypedef signed long long int Signed;との定義Unsignedにしますtypedef unsigned long long int Unsigned;

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>


//  Define the signed and unsigned types we wish to use.
typedef signed char   Signed;
typedef unsigned char Unsigned;

//  uHalfModulus is half the modulus of the unsigned type.
static const Unsigned uHalfModulus = UCHAR_MAX/2+1;

//  sHalfModulus is the negation of half the modulus of the unsigned type.
static const Signed   sHalfModulus = -1 - (Signed) (UCHAR_MAX/2);


/*  Map the unsigned value to the signed value that is the same modulo the
    modulus of the unsigned type.  If the input x maps to a positive value, we
    simply return x.  If it maps to a negative value, we return x minus the
    modulus of the unsigned type.

    In most C implementations, this routine could simply be "return x;".
    However, this version uses several steps to convert x to a negative value
    so that overflow is avoided.
*/
static Signed ConvertToSigned(Unsigned x)
{
    /*  If x is representable in the signed type, return it.  (In some
        implementations, 
    */
    if (x < uHalfModulus)
        return x;

    /*  Otherwise, return x minus the modulus of the unsigned type, taking
        care not to overflow the signed type.
    */
    return (Signed) (x - uHalfModulus) - sHalfModulus;
}


/*  Calculate A*B - C*D given that the result is representable as a Signed
    value.
*/
static signed char Calculate(Signed A, Signed B, Signed C, Signed D)
{
    /*  Map signed values to unsigned values.  Positive values are unaltered.
        Negative values have the modulus of the unsigned type added.  Because
        we do modulo arithmetic below, adding the modulus does not change the
        final result.
    */
    Unsigned a = A;
    Unsigned b = B;
    Unsigned c = C;
    Unsigned d = D;

    //  Calculate with modulo arithmetic.
    Unsigned t = a*b - c*d;

    //  Map the unsigned value to the corresponding signed value.
    return ConvertToSigned(t);
}


int main()
{
    //  Test every combination of inputs for signed char.
    for (int A = SCHAR_MIN; A <= SCHAR_MAX; ++A)
    for (int B = SCHAR_MIN; B <= SCHAR_MAX; ++B)
    for (int C = SCHAR_MIN; C <= SCHAR_MAX; ++C)
    for (int D = SCHAR_MIN; D <= SCHAR_MAX; ++D)
    {
        //  Use int to calculate the expected result.
        int t0 = A*B - C*D;

        //  If the result is not representable in signed char, skip this case.
        if (t0 < SCHAR_MIN || SCHAR_MAX < t0)
            continue;

        //  Calculate the result with the sample code.
        int t1 = Calculate(A, B, C, D);

        //  Test the result for errors.
        if (t0 != t1)
        {
            printf("%d*%d - %d*%d = %d, but %d was returned.\n",
                A, B, C, D, t0, t1);
            exit(EXIT_FAILURE);
        }
    }
    return 0;
}

2

オーバーフローしない小さなコンポーネントに方程式を分解してみることができます。

AB - CD
= [ A(B - N) - C( D - M )] + [AN - CM]

= ( AK - CJ ) + ( AN - CM)

    where K = B - N
          J = D - M

コンポーネントがまだオーバーフローしている場合は、それらを小さなコンポーネントに再帰的に分割してから再結合できます。


これは正しい場合と正しくない場合がありますが、紛らわしいです。あなたはKandを定義しJ、なぜnot NとしMます。また、あなたは方程式をより大きな部分に分解していると思います。ステップ3はOPの質問と同じですが、より複雑です(AK-CJ)->(AB-CD)
Mooing Duck

Nは何からも単純化されていません。それは、それを小さくするためにAから差し引かれた数です。実際、これはpaquetpと似ていますが劣っているソリューションです。ここでは、整数除算の代わりに減算を使用して小さくしています。
ブラッドゴーサーフィン

2

私はすべてのエッジケースをカバーしていなかったし、これを厳密にテストしていなかったかもしれませんが、これは、16ビットCPUで32ビット整数演算を実行しようとしたときに80年代に使用したことを覚えているテクニックを実装します。基本的に、32ビットを2つの16ビット単位に分割し、それらを個別に操作します。

public class DoubleMaths {
  private static class SplitLong {
    // High half (or integral part).
    private final long h;
    // Low half.
    private final long l;
    // Split.
    private static final int SPLIT = (Long.SIZE / 2);

    // Make from an existing pair.
    private SplitLong(long h, long l) {
      // Let l overflow into h.
      this.h = h + (l >> SPLIT);
      this.l = l % (1l << SPLIT);
    }

    public SplitLong(long v) {
      h = v >> SPLIT;
      l = v % (1l << SPLIT);
    }

    public long longValue() {
      return (h << SPLIT) + l;
    }

    public SplitLong add ( SplitLong b ) {
      // TODO: Check for overflow.
      return new SplitLong ( longValue() + b.longValue() );
    }

    public SplitLong sub ( SplitLong b ) {
      // TODO: Check for overflow.
      return new SplitLong ( longValue() - b.longValue() );
    }

    public SplitLong mul ( SplitLong b ) {
      /*
       * e.g. 10 * 15 = 150
       * 
       * Divide 10 and 15 by 5
       * 
       * 2 * 3 = 5
       * 
       * Must therefore multiply up by 5 * 5 = 25
       * 
       * 5 * 25 = 150
       */
      long lbl = l * b.l;
      long hbh = h * b.h;
      long lbh = l * b.h;
      long hbl = h * b.l;
      return new SplitLong ( lbh + hbl, lbl + hbh );
    }

    @Override
    public String toString () {
      return Long.toHexString(h)+"|"+Long.toHexString(l);
    }
  }

  // I'll use long and int but this can apply just as easily to long-long and long.
  // The aim is to calculate A*B - C*D without overflow.
  static final long A = Long.MAX_VALUE;
  static final long B = Long.MAX_VALUE - 1;
  static final long C = Long.MAX_VALUE;
  static final long D = Long.MAX_VALUE - 2;

  public static void main(String[] args) throws InterruptedException {
    // First do it with BigIntegers to get what the result should be.
    BigInteger a = BigInteger.valueOf(A);
    BigInteger b = BigInteger.valueOf(B);
    BigInteger c = BigInteger.valueOf(C);
    BigInteger d = BigInteger.valueOf(D);
    BigInteger answer = a.multiply(b).subtract(c.multiply(d));
    System.out.println("A*B - C*D = "+answer+" = "+answer.toString(16));

    // Make one and test its integrity.
    SplitLong sla = new SplitLong(A);
    System.out.println("A="+Long.toHexString(A)+" ("+sla.toString()+") = "+Long.toHexString(sla.longValue()));

    // Start small.
    SplitLong sl10 = new SplitLong(10);
    SplitLong sl15 = new SplitLong(15);
    SplitLong sl150 = sl10.mul(sl15);
    System.out.println("10="+sl10.longValue()+"("+sl10.toString()+") * 15="+sl15.longValue()+"("+sl15.toString()+") = "+sl150.longValue() + " ("+sl150.toString()+")");

    // The real thing.
    SplitLong slb = new SplitLong(B);
    SplitLong slc = new SplitLong(C);
    SplitLong sld = new SplitLong(D);
    System.out.println("B="+Long.toHexString(B)+" ("+slb.toString()+") = "+Long.toHexString(slb.longValue()));
    System.out.println("C="+Long.toHexString(C)+" ("+slc.toString()+") = "+Long.toHexString(slc.longValue()));
    System.out.println("D="+Long.toHexString(D)+" ("+sld.toString()+") = "+Long.toHexString(sld.longValue()));
    SplitLong sanswer = sla.mul(slb).sub(slc.mul(sld));
    System.out.println("A*B - C*D = "+sanswer+" = "+sanswer.longValue());

  }

}

プリント:

A*B - C*D = 9223372036854775807 = 7fffffffffffffff
A=7fffffffffffffff (7fffffff|ffffffff) = 7fffffffffffffff
10=10(0|a) * 15=15(0|f) = 150 (0|96)
B=7ffffffffffffffe (7fffffff|fffffffe) = 7ffffffffffffffe
C=7fffffffffffffff (7fffffff|ffffffff) = 7fffffffffffffff
D=7ffffffffffffffd (7fffffff|fffffffd) = 7ffffffffffffffd
A*B - C*D = 7fffffff|ffffffff = 9223372036854775807

それはそれが働いているように私に見えます。

サインのオーバーフローなど気の利いたものは見逃しましたが、本質はあると思います。


1
これは@Ofirの提案の実装だと思います。
OldCurmudgeon

2

完全を期すために、誰も言及していないため、一部のコンパイラ(GCCなど)は、最近では実際に128ビット整数を提供しています。

したがって、簡単な解決策は次のとおりです。

(long long)((__int128)A * B - (__int128)C * D)

1

AB-CD = (AB-CD) * AC / AC = (B/C-D/A)*A*C。どちらB/CD/Aオーバーフローすることはできないため、(B/C-D/A)最初に計算します。最終的な結果は定義に従ってオーバーフローしないため、残りの乗算を安全に実行し(B/C-D/A)*A*C、必要な結果を計算できます。

入力も非常に小さい場合は、B/CまたはD/Aがオーバーフローする可能性があること注意してください。可能であれば、入力検査に従ってより複雑な操作が必要になることがあります。


2
整数除算が情報(結果の一部)を失うため、これは機能しません
Ofir

@Ofirは正しいですが、ケーキを食べてそのままにすることはできません。正確に支払うか、(回答で提案したように)追加のリソースを使用して支払う必要があります。私の答えは数学的な性質のものであり、あなたの答えはコンピュータ指向です。それぞれが状況に応じて正しい場合があります。
SomeWittyUsername

2
あなたは正しいです-私はそれを言ったはずです-数学は正しいので、うまくいかないのではなく正確な結果を与えません。ただし、質問の送信者が関心を持つ可能性のあるケース(たとえば、質問の例)では、エラーはおそらく驚くほど大きく、実際のアプリケーションで許容できるよりもはるかに大きくなります。いずれにせよ-それは洞察に満ちた答えであり、私はその言語を使用するべきではなかった。
Ofir

@あなたの言語が不適切だったとは思わない。OPは、「正しい」計算を明らかに要求しました。極端なリソース制約の下で実行されるために精度を失うものではありません。
user4815162342

1

選択してくださいK = a big number(例。K = A - sqrt(A)

A*B - C*D = (A-K)*(B-K) - (C-K)*(D-K) + K*(A-C+B-D); // Avoid overflow.

どうして?

(A-K)*(B-K) = A*B - K*(A+B) + K^2
(C-K)*(D-K) = C*D - K*(C+D) + K^2

=>
(A-K)*(B-K) - (C-K)*(D-K) = A*B - K*(A+B) + K^2 - {C*D - K*(C+D) + K^2}
(A-K)*(B-K) - (C-K)*(D-K) = A*B - C*D - K*(A+B) + K*(C+D) + K^2 - K^2
(A-K)*(B-K) - (C-K)*(D-K) = A*B - C*D - K*(A+B-C-D)

=>
A*B - C*D = (A-K)*(B-K) - (C-K)*(D-K) + K*(A+B-C-D)

=>
A*B - C*D = (A-K)*(B-K) - (C-K)*(D-K) + K*(A-C+B-D)

A、B、C、Dは大きな数であるためA-CB-D小さい数。


実用的にはどのようにKを選びますか?さらに、K *(A-C + BD)はまだオーバーフローする可能性があります。
ylc

@ylc:K = sqrt(A)を選択しA-C+B-Dます。これは小さな数値ではありません。A、B、C、Dは大きな数なので、ACは小さな数です。
アミールサニヤン

K = sqrt(A)を選択すると、(AK)*(BK)が再びオーバーフローする可能性があります。
ylc

@ylc:OK!私はそれをA - sqrt(A):) に変更します
アミールサニヤン

その後 K *(A-C + BD)がオーバーフローする可能性があります。
ylc
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.