كيف يمكنني التحقق مما إذا كان ضرب رقمين في جافا سيؤدي إلى تجاوز فرط؟
-
11-09-2019 - |
سؤال
أريد التعامل مع الحالة الخاصة حيث يتسبب مضاعفة رقمين معا في تجاوز تجاوز. يبدو الرمز شيئا مثل هذا:
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)