كيف يمكنني التحقق مما إذا كان ضرب رقمين في جافا سيؤدي إلى تجاوز فرط؟

StackOverflow https://stackoverflow.com/questions/1657834

سؤال

أريد التعامل مع الحالة الخاصة حيث يتسبب مضاعفة رقمين معا في تجاوز تجاوز. يبدو الرمز شيئا مثل هذا:

int a = 20;
long b = 30;

// if a or b are big enough, this result will silently overflow
long c = a * b;

هذا إصدار مبسط. في البرنامج الحقيقي a و b يتم المصادر في أي مكان آخر في وقت التشغيل. ما أريد تحقيقه هو شيء مثل هذا:

long c;
if (a * b will overflow) {
    c = Long.MAX_VALUE;
} else {
    c = a * b;
}

كيف تقترح أنني أفضل رمز هذا؟

تحديث: a و b هي دائما غير سلبية في سيناريو.

هل كانت مفيدة؟

المحلول

جاوة 8 لديه Math.multiplyExact, Math.addExact وما إلى ذلك للمجلس الدولي والمدة. هذه رمي دون معرفة ArithmeticException على الفائض.

نصائح أخرى

إذا a و b كلاهما إيجابي ثم يمكنك استخدام:

if (a != 0 && b > Long.MAX_VALUE / a) {
    // Overflow
}

إذا كنت بحاجة إلى التعامل مع الأرقام الإيجابية والسلبية، فهذا أكثر تعقيدا:

long maximum = Long.signum(a) == Long.signum(b) ? Long.MAX_VALUE : Long.MIN_VALUE;

if (a != 0 && (b > 0 && b > maximum / a ||
               b < 0 && b < maximum / a))
{
    // Overflow
}

إليك القليل من الطاولة التي قمت بجلدها للتحقق من ذلك، مما أدى إلى أن تجاوز الفائض يحدث في -10 أو +10:

a =  5   b =  2     2 >  10 /  5
a =  2   b =  5     5 >  10 /  2
a = -5   b =  2     2 > -10 / -5
a = -2   b =  5     5 > -10 / -2
a =  5   b = -2    -2 < -10 /  5
a =  2   b = -5    -5 < -10 /  2
a = -5   b = -2    -2 <  10 / -5
a = -2   b = -5    -5 <  10 / -2

هناك مكتبات Java التي توفر عمليات حسابية آمنة، والتي تحقق من تجاوز / اضطرابات طويلة. على سبيل المثال، جوافة longmath.checkedmultiply (طويل أ، طويل ب) إرجاع نتاج a و b, ، شريطة أنه لا يتفوق، والطرق ArithmeticException إذا a * b يفيض في الموقع long علم الحساب.

يمكنك استخدام java.math.bigitteger بدلا من ذلك وتحقق من حجم النتيجة (لم يختبر الرمز):

BigInteger bigC = BigInteger.valueOf(a) * multiply(BigInteger.valueOf(b));
if(bigC.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
  c = Long.MAX_VALUE;
} else {
  c = bigC.longValue()
}

استخدم اللوغاريتمي للتحقق من حجم النتيجة.

هل جافا لديه شيء مثل int.maxvalue؟ إذا كانت الإجابة بنعم، ثم حاول

if (b != 0 && Math.abs(a) > Math.abs(Long.MAX_VALUE / b))
{
 // it will overflow
}

تحرير: ينظر long.max_value في السؤال

سرقت من JRURY.

    long result = a * b;
    if (a != 0 && result / a != b) {
       // overflow
    }

تحديث: هذا الرمز قصير ويعمل جيدا؛ ومع ذلك، فإنه فشل في = -1، B = Long.min_Value.

تعزيز واحد ممكن:

long result = a * b;
if( (Math.signum(a) * Math.signum(b) != Math.signum(result)) || 
    (a != 0L && result / a != b)) {
    // overflow
}

لاحظ أن هذا سيحصل على بعض الفائض دون أي قسم.

ها هي أبسط طريقة يمكنني التفكير فيها

int a = 20;
long b = 30;
long c = a * b;

if(c / b == a) {
   // Everything fine.....no overflow
} else {
   // Overflow case, because in case of overflow "c/b" can't equal "a"
}

لست متأكدا من السبب في أن لا أحد يبحث في محلول مثل:

if (Long.MAX_VALUE/a > b) {
     // overflows
} 

اختيار أن تكون أكبر من الرقمين.

أود أن أبني إجابة جون كوجولمان دون استبداله عن طريق تحريره مباشرة. وهو يعمل لحالة الاختبار الخاصة به (MIN_VALUE = -10, MAX_VALUE = 10) بسبب التماثل MIN_VALUE == -MAX_VALUE, وهذا ليس هو الحال للأعداد الصحيحة تكمل اثنين. في الحقيقة، MIN_VALUE == -MAX_VALUE - 1.

scala> (java.lang.Integer.MIN_VALUE, java.lang.Integer.MAX_VALUE)
res0: (Int, Int) = (-2147483648,2147483647)

scala> (java.lang.Long.MIN_VALUE, java.lang.Long.MAX_VALUE)
res1: (Long, Long) = (-9223372036854775808,9223372036854775807)

عند تطبيقها على الحقيقة MIN_VALUE و MAX_VALUE, ، إجابة جون كوجولمان تعطي حالة تجاوز الفائض عندما a == -1 و b == أي شيء آخر (نقطة رفعها لأول مرة بواسطة كايل). إليك طريقة لإصلاحها:

long maximum = Long.signum(a) == Long.signum(b) ? Long.MAX_VALUE : Long.MIN_VALUE;

if ((a == -1 && b == Long.MIN_VALUE) ||
    (a != -1 && a != 0 && ((b > 0 && b > maximum / a) ||
                           (b < 0 && b < maximum / a))))
{
    // Overflow
}

إنه ليس حل عام لأي MIN_VALUE و MAX_VALUE, ، لكنه عام لجافا Long و Integer وأي قيمة a و b.

يمكن:

if(b!= 0 && a * b / b != a) //overflow

لست متأكدا من هذا "الحل".

تحرير: أضيفت ب! = 0.

قبل أن تراجع: لن يتم تحسين A * B / B. هذا سيكون خطأ مترجم. ما زلت لا أرى حالة حيث يمكن أن يكون خطأ تجاوز الفائض.

ربما هذا سوف تساعدك:

/**
 * @throws ArithmeticException on integer overflow
 */
static long multiply(long a, long b) {
    double c = (double) a * b;
    long d = a * b;

    if ((long) c != d) {
        throw new ArithmeticException("int overflow");
    } else {
        return d;
    }
}

كما تمت الإشارة إليه، فإن Java 8 لديه أساليب Math.xxxexact التي ترمي الاستثناءات على تجاوز الفائض.

إذا كنت لا تستخدم Java 8 لمشروعك، فلا يزال بإمكانك "استعارة" تطبيقاتها التي هي مدمجة للغاية.

فيما يلي بعض الروابط لهذه التطبيقات على موقع على شبكة الإنترنت في الطرف الثالث، لا ضمان ما إذا كانت هذه ستبقى صالحة ولكن في أي حال، يجب أن تكون قادرا على الذهاب إلى مصدر JDK ومعرفة كيفية قيامهم بسحرهم داخل java.lang.Math صف دراسي.

Math.multiplyExact(long, long) http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/math.java؟av=f#882.

Math.addExact http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/math.java؟av=f#805.

الخ، إلخ

C / C ++ (طويل * طويل):

const int64_ w = (int64_) a * (int64_) b;    
if ((long) (w >> sizeof(long) * 8) != (long) w >> (sizeof(long) * 8 - 1))
    // overflow

جافا (int * int، آسف لم أجد INT64 في جافا):

const long w = (long) a * (long) b;    
int bits = 32; // int is 32bits in java    
if ( (int) (w >> bits) != (int) (w >> (bits - 1))) {
   // overflow
}

1. ستفيد النتيجة في نوع كبير (INT * INT وضع النتيجة لفترة طويلة، طويلة * وضعت لفترة طويلة إلى Int64)

2.CMP النتائج >> البتات والنتيجة >> (بت - 1)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top