C / C ++ में पूर्णांक विभाजन की तीव्र छत


262

पूर्णांक मानों को देखते हुए xऔर y, C और C ++ दोनों q = x/yफ्लोटिंग पॉइंट के तल के भागफल के रूप में लौटते हैं । मुझे इसके बजाय छत को वापस करने की एक विधि में दिलचस्पी है। उदाहरण के लिए, ceil(10/5)=2और ceil(11/5)=3

स्पष्ट दृष्टिकोण में कुछ शामिल है:

q = x / y;
if (q * y < x) ++q;

इसके लिए एक अतिरिक्त तुलना और गुणा की आवश्यकता होती है; और अन्य विधियों को मैंने देखा है (वास्तव में उपयोग किया गया है) में कास्टिंग शामिल है floatया double। क्या एक अधिक प्रत्यक्ष विधि है जो अतिरिक्त गुणा (या एक दूसरा विभाजन) और शाखा से बचती है, और वह भी एक फ़्लोटिंग पॉइंट नंबर के रूप में कास्टिंग से बचती है?


70
विभाजन निर्देश अक्सर दोनों भागफल और एक ही समय में शेष रहता है, इसलिए गुणा करने की कोई आवश्यकता नहीं है, बस q = x/y + (x % y != 0);पर्याप्त है
phuclv

2
@ LưuV LnhPhúc उस टिप्पणी को स्वीकृत उत्तर, imo होना चाहिए।
एंड्रियास ग्रेपेंटिन

1
@ LưuV LnhPhúc गंभीरता से आपको उत्तर के रूप में इसे जोड़ना होगा। मैंने सिर्फ एक कोडलेस टेस्ट के दौरान अपने जवाब के लिए इसका इस्तेमाल किया। यह एक आकर्षण की तरह काम करता है, हालांकि मुझे यकीन नहीं है कि उत्तर का आधुनिक भाग कैसे काम करता है लेकिन इसने काम किया।
ज़ाचरी क्रैस

2
@AndreasGrapentin मिगुएल फिग्यूएरेडो द्वारा नीचे दिए गए उत्तर को Loou Vcnh Phúc के ऊपर टिप्पणी छोड़ने से लगभग एक साल पहले प्रस्तुत किया गया था। जबकि मैं समझता हूं कि मिगुएल का समाधान कितना आकर्षक और सुरुचिपूर्ण है, मैं इस विलंबित तारीख में स्वीकृत उत्तर को बदलने के लिए इच्छुक नहीं हूं। दोनों दृष्टिकोण ध्वनि बने हुए हैं। यदि आप इसके बारे में पर्याप्त रूप से महसूस करते हैं, तो मेरा सुझाव है कि आप नीचे दिए गए मिगुएल के उत्तर-अप वोटिंग द्वारा अपना समर्थन दिखाएं।
औरंद

1
अजीब बात है, मैंने प्रस्तावित समाधानों का कोई भी माप या विश्लेषण नहीं देखा है। आप निकट-हड्डी पर गति के बारे में बात करते हैं, लेकिन आर्किटेक्चर, पाइपलाइन, शाखाओं के निर्देश और घड़ी चक्र की कोई चर्चा नहीं है।
राडो

जवाबों:


394

सकारात्मक संख्या के लिए

unsigned int x, y, q;

पूर्ण करने के लिए ...

q = (x + y - 1) / y;

या (एक्स + वाई में अतिप्रवाह से बचना)

q = 1 + ((x - 1) / y); // if x != 0

6
@bitc: नकारात्मक संख्याओं के लिए, मेरा मानना ​​है कि C99 गोल-से-शून्य निर्दिष्ट करता है, इसलिए x/yविभाजन की छत है। C90 ने गोल करने के तरीके को निर्दिष्ट नहीं किया, और मुझे नहीं लगता कि वर्तमान C ++ मानक या तो करता है।
डेविड थॉर्नले

6
एरिक
लिपर्ट

3
नोट: यह अतिप्रवाह हो सकता है। q = ((long long) x + y - 1) / y नहीं होगा। मेरा कोड हालांकि धीमा है, इसलिए यदि आप जानते हैं कि आपके नंबर ओवरफ्लो नहीं होंगे, तो आपको स्पार्की के संस्करण का उपयोग करना चाहिए।
जोर्जेन फॉग

1
@bitc: मेरा मानना ​​है कि डेविड की बात यह थी कि यदि परिणाम नकारात्मक है तो आप ऊपर की गणना का उपयोग नहीं करेंगे - आप बस इस्तेमाल करेंगेq = x / y;
कैफ़े

12
दूसरे में एक समस्या है जहां x 0. Ceil (0 / y) = 0 है, लेकिन यह 1. वापस आता है।
Omry Yadan

78

सकारात्मक संख्या के लिए:

    q = x/y + (x % y != 0);

5
सबसे आम वास्तुकला का विभाजन निर्देश भी इसके परिणाम में शेष है, इसलिए इसे वास्तव में केवल एक विभाजन की आवश्यकता है और बहुत तेज़ होगा
phuclv

58

इस समस्या को हल करने के लिए स्पार्की का जवाब एक मानक तरीका है, लेकिन जैसा कि मैंने अपनी टिप्पणी में भी लिखा था, आप ओवरफ्लो का जोखिम उठाते हैं। यह एक व्यापक प्रकार का उपयोग करके हल किया जा सकता है, लेकिन क्या होगा यदि आप long longएस को विभाजित करना चाहते हैं ?

नाथन अर्नस्ट का जवाब एक समाधान प्रदान करता है, लेकिन इसमें एक फ़ंक्शन कॉल, एक चर घोषणा और एक सशर्त शामिल है, जो इसे ओपीएस कोड से कम नहीं करता है और शायद धीमी भी है, क्योंकि इसे अनुकूलित करना कठिन है।

मेरा समाधान यह है:

q = (x % y) ? x / y + 1 : x / y;

यह ओपीएस कोड की तुलना में थोड़ा तेज होगा, क्योंकि मोड्यूलो और डिवीजन को प्रोसेसर पर एक ही निर्देश का उपयोग करके किया जाता है, क्योंकि कंपाइलर देख सकता है कि वे समकक्ष हैं। कम से कम gcc 4.4.1 x86 पर -O2 ध्वज के साथ यह अनुकूलन करता है।

सिद्धांत रूप में संकलक नाथन अर्नस्ट के कोड में फ़ंक्शन कॉल को इनलाइन कर सकता है और एक ही चीज़ का उत्सर्जन कर सकता है, लेकिन जब मैंने इसका परीक्षण किया तो gcc ने ऐसा नहीं किया। यह इसलिए हो सकता है क्योंकि यह मानक पुस्तकालय के एकल संस्करण में संकलित कोड को टाई करेगा।

अंतिम नोट के रूप में, आधुनिक मशीन पर इस मामले में कोई भी मायने नहीं रखता है, सिवाय इसके कि आप बेहद तंग पाश में हैं और आपका सारा डेटा रजिस्टरों या एल 1-कैश में है। अन्यथा ये सभी समाधान समान रूप से तेज़ होंगे, संभवतः नाथन अर्न्स्ट के अलावा, जो कि मुख्य मेमोरी से फ़ंक्शन को प्राप्त करने के लिए काफी धीमा हो सकता है।


3
ओवरफ्लो को ठीक करने का एक आसान तरीका था, बस y / y को कम करना:q = (x > 0)? 1 + (x - 1)/y: (x / y);
बेन वायगेट

-1: यह एक अक्षम्य तरीका है, क्योंकि यह महंगा% के लिए एक सस्ता * ट्रेड करता है; ओपी दृष्टिकोण से भी बदतर।
यवेस डाएव

2
नहीं, यह नहीं है। जैसा कि मैंने उत्तर में बताया है, जब आप पहले से ही डिवीजन करते हैं तो% ऑपरेटर मुक्त होता है।
जोर्जेन फॉग

1
तब अभिव्यक्ति की q = x / y + (x % y > 0);तुलना में आसान है ? :?
हान

यह इस बात पर निर्भर करता है कि आप "आसान" से क्या मतलब है। कंपाइलर इसका अनुवाद कैसे करता है, यह इस पर निर्भर करता है कि यह तेज हो भी सकता है और नहीं भी। मेरा अनुमान धीमा होगा लेकिन मुझे यह सुनिश्चित करने के लिए मापना होगा।
जोर्जेन फॉग

18

आप divcddlib में फ़ंक्शन का उपयोग एक कॉल में भागफल और शेष प्राप्त करने के लिए कर सकते हैं और फिर अलग से छत को संभाल सकते हैं, जैसे नीचे

#include <cstdlib>
#include <iostream>

int div_ceil(int numerator, int denominator)
{
        std::div_t res = std::div(numerator, denominator);
        return res.rem ? (res.quot + 1) : res.quot;
}

int main(int, const char**)
{
        std::cout << "10 / 5 = " << div_ceil(10, 5) << std::endl;
        std::cout << "11 / 5 = " << div_ceil(11, 5) << std::endl;

        return 0;
}

12
डबल धमाके के एक दिलचस्प मामले के रूप में, आप भी return res.quot + !!res.rem;:)
सैम हरवेल

क्या ldiv हमेशा तर्कों को लंबे समय तक बढ़ावा नहीं देता है? और वह कुछ भी खर्च नहीं करता है, अप-कास्टिंग या डाउन-कास्टिंग?
einpoklum

12

इस बारे में कैसा है? (इसके लिए y गैर-नकारात्मक की आवश्यकता होती है, इसलिए इस दुर्लभ स्थिति में इसका उपयोग न करें जहां y एक ऐसा संस्करण है जिसमें कोई गैर-नकारात्मकता की गारंटी नहीं है)

q = (x > 0)? 1 + (x - 1)/y: (x / y);

मैंने y/yएक को कम कर दिया, शब्द को समाप्त कर दिया x + y - 1और इसके साथ अतिप्रवाह का कोई भी मौका।

x - 1जब xएक अहस्ताक्षरित प्रकार होता है और शून्य होता है, तो मैं इधर-उधर लपेटने से बचता हूं ।

हस्ताक्षरित के लिए x, नकारात्मक और शून्य अभी भी एक ही मामले में संयोजित होते हैं।

संभवतः आधुनिक सामान्य-उद्देश्य सीपीयू पर बहुत बड़ा लाभ नहीं है, लेकिन यह अन्य सही उत्तरों की तुलना में एम्बेडेड सिस्टम में कहीं अधिक तेज होगा।


आपका बाकी हमेशा 0 वापस करेगा, कुछ भी गणना करने की आवश्यकता नहीं है।
रूड अल्थुइज़न

@Rudud: सच नहीं है। X = -45 और y = 4 पर विचार करें
बेन वायगेट

7

सकारात्मक और नकारात्मक दोनों के लिए एक समाधान है xलेकिन केवल y1 विभाजन के साथ और शाखाओं के बिना सकारात्मक के लिए :

int ceil(int x, int y) {
    return x / y + (x % y > 0);
}

ध्यान दें, यदि xसकारात्मक है तो विभाजन शून्य की ओर है, और हमें 1 जोड़ना चाहिए यदि अनुस्मारक शून्य नहीं है।

यदि xनकारात्मक है तो विभाजन शून्य की ओर है, यही हमें चाहिए, और हम कुछ भी नहीं जोड़ेंगे क्योंकि x % yसकारात्मक नहीं है


दिलचस्प है, क्योंकि y के साथ सामान्य मामले निरंतर हैं
वुल्फ

1
mod के लिए विभाजन की आवश्यकता होती है, इसलिए यहाँ केवल 1 विभाजन नहीं है, लेकिन शायद एक में दो समान विभाजनों का अनुकूलन हो सकता है।
एम। केज़म अखगीरी

4

यह सकारात्मक या नकारात्मक संख्याओं के लिए काम करता है:

q = x / y + ((x % y != 0) ? !((x > 0) ^ (y > 0)) : 0);

अगर वहाँ एक शेष है इसकी जांच करता है देखने के लिए xऔर yएक ही हस्ताक्षर के हैं और कहते हैं 1तदनुसार।


3

मैंने बल्कि टिप्पणी की होगी, लेकिन मेरे पास पर्याप्त उच्च प्रतिनिधि नहीं है।

जहां तक ​​मुझे पता है, सकारात्मक तर्क और एक भाजक के लिए जो 2 की शक्ति है, यह सबसे तेज़ तरीका है (CUDA में परीक्षण):

//example y=8
q = (x >> 3) + !!(x & 7);

केवल सामान्य सकारात्मक तर्कों के लिए, मैं इसे ऐसे करना चाहता हूं:

q = x/y + !!(x % y);

यह कैसे को देखने के लिए दिलचस्प हो जाएगा q = x/y + !!(x % y);के खिलाफ को पीछे छोड़ देता q = x/y + (x % y == 0);है और q = (x + y - 1) / y;समाधान प्रदर्शन के लिहाज से समकालीन CUDA में।
ग्रेग क्रेमिडा

2

सरलीकृत सामान्य रूप,

int div_up(int n, int d) {
    return n / d + (((n < 0) ^ (d > 0)) && (n % d));
} //i.e. +1 iff (not exact int && positive result)

अधिक सामान्य उत्तर के लिए, C ++ पूर्णांक विभाजन के लिए अच्छी तरह से परिभाषित गोलाई रणनीति के साथ कार्य करता है


-2

O3 के साथ संकलन, संकलक अनुकूलन को अच्छी तरह से करता है।

q = x / y;
if (x % y)  ++q;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.