जावा में दो संख्याओं को गुणा करने पर मैं कैसे जांच सकता हूं कि कोई अतिप्रवाह होगा?


101

मैं उस विशेष मामले को संभालना चाहता हूं जहां दो संख्याओं को एक साथ गुणा करना एक अतिप्रवाह का कारण बनता है। कोड कुछ इस तरह दिखता है:

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मेरे परिदृश्य में हमेशा गैर-नकारात्मक होते हैं।


6
यह बहुत बुरा है कि जावा सीपीयू के अतिप्रवाह झंडे को अप्रत्यक्ष पहुंच प्रदान नहीं करता है , जैसा कि C # में किया गया है
ड्रू नोक

जवाबों:


92

जावा 8 है Math.multiplyExact, Math.addExactints के लिए और लंबे समय आदि। ये ArithmeticExceptionओवरफ्लो होने पर अनियंत्रित हो जाते हैं।


59

यदि 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

मुझे यह उल्लेख करना चाहिए कि मेरे परिदृश्य में ए और बी हमेशा गैर-नकारात्मक होते हैं, जो इस दृष्टिकोण को कुछ हद तक सरल करेगा।
स्टीव मैक्लियोड

3
मुझे लगता है कि यह एक मामला विफल हो सकता है: a = -1 और b = 10. Integer.MIN_VALUE में अधिकतम / एक अभिव्यक्ति परिणाम और जब कोई मौजूद नहीं है तो अतिप्रवाह का पता लगाता है
काइल

यह वास्तव में अच्छा है। सोच वालों के लिए, कारण यह काम करता है कि पूर्णांक के लिए है n, n > xके रूप में ही है n > floor(x)। धनात्मक पूर्णांक विभाजन के लिए एक अंतर्निहित तल होता है। (नकारात्मक संख्या के लिए इसके बजाय गोल हो जाता है)
थॉमस अहले

a = -1और b = 10समस्या का समाधान करने के लिए , नीचे मेरा उत्तर देखें।
जिम पीवार्स्की

17

जावा पुस्तकालय हैं जो सुरक्षित अंकगणितीय संचालन प्रदान करते हैं, जो लंबी अतिप्रवाह / अंडरफ्लो की जांच करते हैं। उदाहरण के लिए, अमरूद का LongMath.checkedMultiply (लॉन्ग ए, लॉन्ग बी) के उत्पाद को लौटाता है aऔर bबशर्ते कि यह ओवरफ्लो नहीं करता है, और ArithmeticExceptionअगर a * bहस्ताक्षरित longअंकगणित में ओवरफ्लो होता है ।


4
यह सबसे अच्छा जवाब है - एक पुस्तकालय का उपयोग करें जो कि उन लोगों द्वारा कार्यान्वित किया गया था जो वास्तव में जावा में मशीन अंकगणित को समझते हैं और जिसे बहुत से लोगों द्वारा परीक्षण किया गया है। अपने स्वयं के लिखने या अन्य उत्तरों में पोस्ट किए गए किसी भी आधे-बेक किए गए कोड का उपयोग करने का प्रयास न करें!
रिच

@Enerccio - मुझे आपकी टिप्पणी समझ नहीं आ रही है। क्या आप कह रहे हैं कि अमरूद सभी प्रणालियों पर काम नहीं करेगा? मैं आपको आश्वासन दे सकता हूं कि यह हर जगह काम करेगा जो जावा करता है। क्या आप कह रहे हैं कि कोड का पुन: उपयोग करना सामान्य विचार है? मैं असहमत हूँ तो।
रिच

2
@ मैं विशाल पुस्तकालय सहित कह रहा हूं ताकि आप एक फ़ंक्शन का उपयोग कर सकें यह एक बुरा विचार है।
Enerccio

क्यों? यदि आप एक बड़े आवेदन को लिख रहे हैं, उदाहरण के लिए एक व्यापार के लिए, तो क्लासपाथ पर एक अतिरिक्त जार कोई नुकसान नहीं करेगा और अमरूद में बहुत उपयोगी कोड हैं। अपने सावधानीपूर्वक परीक्षण किए गए कोड को पुन: उपयोग करने से बेहतर है कि आप अपना स्वयं का संस्करण लिखने की कोशिश करें (जो मुझे लगता है कि आप क्या सलाह देते हैं?)। यदि आप एक ऐसे वातावरण में लिख रहे हैं, जहां एक अतिरिक्त जार बहुत महंगा होने वाला है (जहाँ? एम्बेडेड जावा?) तो शायद आपको अमरूद से सिर्फ इस वर्ग को निकालना चाहिए। क्या अमरूद के सावधानीपूर्वक परीक्षण किए गए कोड की नकल करने की तुलना में स्टैकऑवरफ्लो से अप्रयुक्त उत्तर की नकल करना बेहतर है?
रिच

एक सशर्त कुछ के लिए एक अपवाद को फेंक नहीं है अगर एक सशर्त अगर-तो संभाल सकता है?
14

6

आप इसके बजाय java.math.BigInteger का उपयोग कर सकते हैं और परिणाम का आकार जांचें (कोड का परीक्षण नहीं किया है):

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()
}

7
मैं इस समाधान को धीमी गति से
ढूँढता हूं

यह शायद यह करने का सबसे अच्छा तरीका है, हालांकि। मैंने माना कि यह एक संख्यात्मक अनुप्रयोग था, यही कारण है कि मैंने इसे ऑफहैंड करने की सिफारिश नहीं की थी, लेकिन यह वास्तव में इस समस्या को हल करने का सबसे अच्छा तरीका है।
स्टीफन केंडल

2
मुझे यकीन नहीं है कि आप BigInteger के साथ '>' ऑपरेटर का उपयोग कर सकते हैं। तुलना विधि का उपयोग किया जाना चाहिए।
पियरे

तुलना में बदल गया है, और गति मायने रखती है या नहीं, उन परिस्थितियों पर निर्भर करती है जहां कोड का उपयोग किया जाएगा।
उफिल लिंडबैक

5

परिणाम के आकार की जांच करने के लिए लघुगणक का उपयोग करें।


क्या आपका मतलब है ceil(log(a)) + ceil(log(b)) > log(Long.MAX):?
थॉमस जुंग

1
मैंने इसे जाँचा था। छोटे मूल्यों के लिए यह BigInteger की तुलना में 20% अधिक तेज है और MAX के निकट मूल्यों के लिए यह लगभग (5% अधिक तेज) है। Yossarian का कोड सबसे तेज (95% और 75% तेजी से BigInteger) है।
थॉमस जुंग

मुझे संदेह है कि यह कुछ मामलों में विफल हो सकता है।
टॉम हैटिन -

याद रखें कि एक पूर्णांक लॉग प्रभावी रूप से अग्रणी शून्य की संख्या की गिनती कर रहा है, और आप कुछ सामान्य मामलों को अनुकूलित कर सकते हैं (जैसे यदि ((| b) और 0xffffff00000000L) == 0) तो आप जानते हैं कि आप सुरक्षित हैं)। दूसरी ओर, जब तक आप सामान्य मामलों के लिए 30/40-ईश घड़ी चक्रों के लिए अपना अनुकूलन नीचे नहीं प्राप्त कर सकते, जॉन कुगेलमैन की विधि शायद बेहतर प्रदर्शन करेगी (एक पूर्णांक विभाजन सी है। 2 बिट्स / घड़ी चक्र जैसा कि मुझे याद है)।
नील कॉफ़ी

पीएस सोर्र, मुझे लगता है कि मुझे एंड मास्क (0xffffffff80000000L) में एक अतिरिक्त बिट सेट की आवश्यकता है - यह थोड़ी देर है, लेकिन आपको यह विचार मिलता है ...
नील कॉफ़ी

4

क्या Java में int.MaxValue जैसा कुछ है? यदि हाँ, तो प्रयास करें

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

संपादित करें: प्रश्न में Long.MAX_VALUE देखा गया


मैं नीचे नहीं था, लेकिन Math.Abs(a)अगर यह काम नहीं करता aहै Long.MIN_VALUE
जॉन कुगेलमैन

@ जॉन - ए और बी> हैं। 0. मुझे लगता है कि योसेरियन का दृष्टिकोण (b! = 0 && a> Long.MAX_VALUE / b) सबसे अच्छा है।
थॉमस जुंग

@ थोमस, ए और बी हैं = = 0, यानी गैर-नकारात्मक।
स्टीव मैक्लियोड

2
सही है, लेकिन उस मामले में एब्स की कोई जरूरत नहीं है। यदि नकारात्मक संख्याओं की अनुमति है, तो यह कम से कम एक किनारे के मामले में विफल रहता है। मैं बस इतना ही कह रहा हूं, सिर्फ निपिकी।
जॉन कुगेलमैन

जावा में आपको Math.abs का उपयोग करना चाहिए, Math.Abs ​​(C # guy?)
dfa

4

यहां मैं सबसे सरल तरीका सोच सकता हूं

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"
}

3

जूसी से चोरी

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

अद्यतन: यह कोड छोटा है और अच्छी तरह से काम करता है; हालाँकि, यह a = -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
}

ध्यान दें कि यह बिना किसी विभाजन के कुछ ओवरफ्लो को पकड़ेगा।


आप Math.signum के बजाय Long.signum का उपयोग कर सकते हैं
aditsu इसलिए छोड़ें क्योंकि SE EVIL

3

जैसा कि बताया गया है, Java 8 में Math.xxxExact मेथड है जो अपवादों को ओवरफ्लो पर फेंकते हैं।

यदि आप अपनी परियोजना के लिए जावा 8 का उपयोग नहीं कर रहे हैं, तो आप अभी भी उनके कार्यान्वयन को "उधार" ले सकते हैं जो बहुत कॉम्पैक्ट हैं।

JDK स्रोत कोड रिपॉजिटरी में इन कार्यान्वयनों के कुछ लिंक दिए गए हैं, इस बात की कोई गारंटी नहीं है कि ये मान्य रहेंगे या नहीं, लेकिन किसी भी स्थिति में आपको JDK स्रोत डाउनलोड करने में सक्षम होना चाहिए और देखना होगा कि वे java.lang.Mathकक्षा के अंदर अपना जादू कैसे करते हैं ।

Math.multiplyExact(long, long) http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/java/lang/Math.java#l925

Math.addExact(long, long) http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/java/lang/Math.java#l830

आदि आदि।

अद्यतन: ओपन जेडीके के मर्क्यूरियल रिपॉजिटरी के लिंक के लिए 3 पार्टी वेबसाइट पर अमान्य लिंक को बंद कर दिया।


2

मुझे यकीन नहीं है कि कोई भी इस तरह समाधान क्यों नहीं देख रहा है:

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

दो संख्याओं में से बड़ा होना चुनें।


2
मुझे नहीं लगता कि यह aबड़ा या छोटा होता है?
थॉमस अहले

2

मैं जॉन कुगेलमैन के उत्तर को सीधे संपादित करके बदले बिना बनाना चाहता हूं। यह उसके परीक्षण मामले ( 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


मैंने सिर्फ यह सोचा कि यह अनावश्यक रूप से जटिल हो जाएगा, क्योंकि यह समाधान केवल तभी काम करता है MIN_VALUE = -MAX_VALUE - 1, न कि किसी अन्य मामले (आपके उदाहरण परीक्षण मामले सहित)। मुझे बहुत कुछ बदलना होगा।
जिम पिवार्स्की

1
मूल पोस्टर की जरूरतों से परे कारणों के लिए; मेरे जैसे लोगों के लिए जिन्हें यह पृष्ठ मिला क्योंकि उन्हें एक अधिक सामान्य मामले के समाधान की आवश्यकता थी (नकारात्मक संख्याओं को संभालना और कड़ाई से जावा 8 को नहीं)। वास्तव में, चूंकि इस समाधान में शुद्ध अंकगणित और तर्क से परे कोई कार्य शामिल नहीं है, इसलिए इसका उपयोग सी या अन्य भाषाओं के लिए भी किया जा सकता है।
जिम पिवार्स्की

1

शायद:

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

इस "समाधान" के बारे में निश्चित नहीं है।

संपादित करें: जोड़ा गया b! = 0।

इससे पहले कि आप डाउनवोट करें : एक * b / b को ऑप्टिमाइज़ नहीं किया जाएगा। यह कंपाइलर बग होगा। मुझे अभी भी ऐसा मामला नहीं दिख रहा है जहां ओवरफ्लो बग को नकाब लगाया जा सके।


जब अतिप्रवाह एक सही लूप का कारण बनता है तब भी विफल रहता है।
स्टीफन केंडल

क्या आपके पास इसका एक उदाहरण है कि आपका क्या मतलब है?
थॉमस जुंग

बस थोड़ा सा परीक्षण लिखा: BigInteger का उपयोग करना इस विभाजन दृष्टिकोण का उपयोग करने से 6 गुना धीमा है। इसलिए मुझे लगता है कि कोने के मामलों के लिए अतिरिक्त जाँच यह प्रदर्शन के लायक है।
मुहल्ला

जावा संकलक के बारे में ज्यादा जानकारी नहीं है, लेकिन एक अभिव्यक्ति की तरह a * b / bसिर्फ aकई अन्य संदर्भों में अनुकूलित होने की संभावना है।
22

TokenMacGuy - इसे इस तरह से अनुकूलित नहीं किया जा सकता है अगर अतिप्रवाह का खतरा है।
टॉम हॉकिन -

1

शायद इससे आपको मदद मिलेगी:

/**
 * @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;
    }
}

एक ऑपरेंड के रूप में मदद नहीं करेगा long
टॉम हॉल्टिन -

1
क्या आपने इसका परीक्षण भी किया? किसी भी बड़े लेकिन ए और बी के मूल्यों के अतिप्रवाह के लिए यह गुणा के दोहरे संस्करण में गोलाई त्रुटियों के कारण विफल होगा (उदाहरण के लिए 123456789123L और 74709314L)। यदि आप मशीन अंकगणित को नहीं समझते हैं, तो इस तरह के सटीक प्रश्न का उत्तर देना उत्तर न देने से भी बदतर है, क्योंकि यह लोगों को गुमराह करेगा।
रिच

-1

c / c ++ (लंबा * लंबा):

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

जावा (int * int, माफ करना, मुझे java में 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)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.