C ++ में फ्लोट के लिए राउंड ()


232

मुझे एक साधारण फ़्लोटिंग पॉइंट राउंडिंग फ़ंक्शन की आवश्यकता है, इस प्रकार:

double round(double);

round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1

मैं गणित में ceil()और पा सकता हूँ floor()- लेकिन नहीं round()

क्या यह मानक C ++ लाइब्रेरी में किसी अन्य नाम के तहत मौजूद है, या यह गायब है ??


1
यदि आप केवल संख्या को एक गोल संख्या के रूप में आउटपुट करना चाहते हैं तो ऐसा लगता है कि आप std::cout << std::fixed << std::setprecision(0) << -0.9उदाहरण के लिए कर सकते हैं ।
फ्रैंक

43
इसे संरक्षित करना ... शानदार नई गोलाई योजनाओं के साथ नए उपयोगकर्ताओं को पहले मौजूदा उत्तरों को पढ़ना चाहिए।
शोग

12
roundC ++ 11 के बाद से उपलब्ध है <cmath>। दुर्भाग्य से अगर आप Microsoft Visual Studio में हैं तो यह अभी भी गायब है: connect.microsoft.com/VisualStudio/feedback/details/775474/…
Alessandro Jacopson

3
जैसा कि मैंने अपने जवाब में ध्यान दिया है, अपने खुद के रोल करने से roundबहुत सारे कैवियट होते हैं। C ++ 11 से पहले, मानक C90 पर निर्भर था जिसमें शामिल नहीं था round। C ++ 11 C99 पर निर्भर करता है जो कि है, roundलेकिन साथ ही जैसा कि मैंने उल्लेख किया है truncजिसमें विभिन्न गुण हैं और आवेदन के आधार पर अधिक उपयुक्त हो सकता है। अधिकांश उत्तर इस बात को भी नजरअंदाज करते हैं कि एक उपयोगकर्ता एक अभिन्न प्रकार की वापसी करना चाह सकता है जिसमें और भी अधिक समस्याएं हैं।
शाफिक यागमोर

2
@uvts_cvs यह दृश्य स्टूडियो के नवीनतम संस्करण के साथ एक मुद्दा नहीं लगता है, इसे लाइव देखें
शाफिक यागमोर

जवाबों:


144

C ++ 98 मानक लाइब्रेरी में कोई राउंड () नहीं है। आप हालांकि खुद लिख सकते हैं। निम्नलिखित राउंड-हाफ-अप का कार्यान्वयन है :

double round(double d)
{
  return floor(d + 0.5);
}

संभावित कारण C ++ 98 मानक पुस्तकालय में कोई गोल कार्य नहीं है, यह वास्तव में विभिन्न तरीकों से लागू किया जा सकता है। उपरोक्त एक सामान्य तरीका है, लेकिन अन्य भी हैं जैसे कि राउंड-टू-सम , जो कम पक्षपाती है और आम तौर पर बेहतर है यदि आप बहुत अधिक राउंडिंग करने जा रहे हैं; हालांकि इसे लागू करना थोड़ा अधिक जटिल है।


53
यह नकारात्मक संख्याओं को सही तरीके से नहीं संभालता है। Litb द्वारा उत्तर सही है।
पंजीकृत उपयोगकर्ता

39
@ इनरजॉइन: हां, यह litb के उत्तर के लिए नकारात्मक संख्याओं को अलग तरीके से संभालता है, लेकिन यह इसे "गलत" नहीं बनाता है।
रोडी

39
ट्रंकटिंग से पहले 0.5 जोड़ने से 0.49999999999999994 सहित कई इनपुट के लिए निकटतम पूर्णांक के लिए राउंड विफल हो जाता है। ब्लॉग
पास्कल कूक

10
@ Sergi0: कोई "सही" और "गलत" नहीं है क्योंकि गोलाई की एक से अधिक परिभाषाएं हैं जो तय करती हैं कि आधे बिंदु पर क्या होता है। निर्णय पारित करने से पहले अपने तथ्यों की जाँच करें।
जॉन

16
@MuhammadAnnaqeeb: आप सही कह रहे हैं, C ++ 11 की रिलीज़ के बाद से चीजों में बहुत सुधार हुआ है। यह सवाल एक अन्य समय में पूछा गया और जवाब दिया गया जब जीवन कठिन था और खुशियाँ कम थीं। यह उन नायकों के लिए एक ode के रूप में बना हुआ है, जो तब जीवित थे और उन गरीब आत्माओं के लिए लड़े थे, जो अभी भी आधुनिक उपकरणों का उपयोग करने में असमर्थ हैं।
एंड्रियास मैग्नेसन

96

बूस्ट राउंडिंग फ़ंक्शन का एक सरल सेट प्रदान करता है।

#include <boost/math/special_functions/round.hpp>

double a = boost::math::round(1.5); // Yields 2.0
int b = boost::math::iround(1.5); // Yields 2 as an integer

अधिक जानकारी के लिए, बूस्ट प्रलेखन देखें ।

संपादित करें : चूंकि सी ++ 11, देखते हैं std::round, std::lroundऔरstd::llround


2
मैं पहले से ही अपने प्रोजेक्ट में बूस्ट का उपयोग कर रहा था, इसके लिए +1, भोले floor(value + 0.5)दृष्टिकोण का उपयोग करने से बेहतर था !
गुस्तावो मैकिएल

@GustavoMaciel मुझे पता है कि मैं खेल के लिए थोड़ा देर से हूं, लेकिन कार्यान्वयन को बढ़ावा देना है floor(value + 0.5)
एन। 'सर्वनाम' मी।

यह वास्तव में नहीं है: github.com/boostorg/math/blob/develop/include/boost/math/… 4 साल बाद, मैं यह भी कहना चाहूंगा कि floor(value + 0.5)यह बिल्कुल भी भोली नहीं है, बल्कि संदर्भ और प्रकृति पर निर्भर करती है आप गोल करना चाहते हैं!
गुस्तावो मैकल

84

C ++ 03 मानक C90 मानक पर निर्भर करता है कि मानक C लाइब्रेरी को क्या कहते हैं, जो प्रारूप C ++ 03 मानक ( निकटतम सार्वजनिक रूप से उपलब्ध मसौदा मानक C ++ 03 के1.2 मानक N1804 ) के मसौदे में शामिल है, अनुभाग सामान्य संदर्भ :

ISO / IEC 9899: 1990 के खंड 7 और ISO / IEC 9899 / Amd.1: 1995 के खंड 7 में वर्णित पुस्तकालय को बाद में मानक C लाइब्रेरी कहा जाता है। 1)

यदि हम c के लिए प्रलेखन के दौर में जाते हैं, तो चारों ओर से घेरे हुए हैं, हम यह देख सकते हैं कि गोल और संबंधित कार्य C99 का हिस्सा हैं और इस प्रकार C ++ 03 या पूर्व में उपलब्ध नहीं होंगे।

C ++ 11 में, C ++ 11 के बाद से यह परिवर्तन C मानक लाइब्रेरी के लिए C99 ड्राफ्ट मानक पर निर्भर करता है और इसलिए std :: दौर प्रदान करता है और इंटीग्रल रिटर्न प्रकारों के लिए std :: lround, std :: llround :

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::round( 0.4 ) << " " << std::lround( 0.4 ) << " " << std::llround( 0.4 ) << std::endl ;
    std::cout << std::round( 0.5 ) << " " << std::lround( 0.5 ) << " " << std::llround( 0.5 ) << std::endl ;
    std::cout << std::round( 0.6 ) << " " << std::lround( 0.6 ) << " " << std::llround( 0.6 ) << std::endl ;
}

C99 से एक अन्य विकल्प भी std :: trunc होगा :

निकटतम पूर्णांक की गणना arg से अधिक परिमाण में नहीं है।

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::trunc( 0.4 ) << std::endl ;
    std::cout << std::trunc( 0.9 ) << std::endl ;
    std::cout << std::trunc( 1.1 ) << std::endl ;

}

आपका सर्वश्रेष्ठ दांव उपयोग होगा यदि आप गैर सी ++ समर्थन करने के लिए 11 आवेदन पत्र की जरूरत है बढ़ावा दौर, iround, lround, llround या बढ़ावा TRUNC

अपने स्वयं के संस्करण को रोल करना कठिन है

अपनी खुद की रोलिंग शायद इस प्रयास के लायक नहीं है क्योंकि हार्डर दिखता है: निकटतम पूर्णांक तक फ्लोटिंग, भाग 1 , निकटतम पूर्णांक के लिए फ्लोटिंग, भाग 2 और निकटतम पूर्णांक के लिए फ़्लोटिंग फ्लोटिंग, भाग 3 की व्याख्या करें:

उदाहरण के लिए एक सामान्य रोल जो आपके कार्यान्वयन का उपयोग करता है std::floorऔर जोड़ना 0.5सभी इनपुट के लिए काम नहीं करता है:

double myround(double d)
{
  return std::floor(d + 0.5);
}

इसके लिए एक इनपुट विफल हो जाएगा 0.49999999999999994, ( इसे लाइव देखें )।

एक अन्य सामान्य कार्यान्वयन में एक अस्थायी बिंदु प्रकार को एक अभिन्न प्रकार में शामिल करना शामिल है, जो उस मामले में अपरिभाषित व्यवहार को आमंत्रित कर सकता है जहां अभिन्न अंग को गंतव्य प्रकार में प्रतिनिधित्व नहीं किया जा सकता है। हम इसे C ++ मानक खंड 4.9 फ्लोटिंग-इंटीग्रल रूपांतरणों के मसौदे से देख सकते हैं, जो कहता है ( जोर मेरा ):

फ़्लोटिंग पॉइंट प्रकार के एक प्रोलव्यू को एक पूर्णांक प्रकार के एक प्रचलन में परिवर्तित किया जा सकता है। रूपांतरण ट्रंकट्स; यानी भिन्नात्मक भाग को छोड़ दिया गया है। यदि गंतव्य प्रकार में काट-छाँट किए गए मान का प्रतिनिधित्व नहीं किया जा सकता है तो व्यवहार अपरिभाषित है। [...]

उदाहरण के लिए:

float myround(float f)
{
  return static_cast<float>( static_cast<unsigned int>( f ) ) ;
}

दिया गया std::numeric_limits<unsigned int>::max()है 4294967295तो निम्नलिखित कॉल:

myround( 4294967296.5f ) 

अतिप्रवाह का कारण होगा, ( इसे लाइव देखें )।

हम देख सकते हैं कि सी में राउंड () को लागू करने के लिए कॉन्सिसेज़ के इस जवाब को देखकर वास्तव में यह कितना मुश्किल है? जो एकल परिशुद्धता फ्लोट दौर के newlibs संस्करण को संदर्भित करता है । यह किसी ऐसी चीज के लिए बहुत लंबा कार्य है जो सरल लगती है। ऐसा लगता है कि अस्थायी बिंदु कार्यान्वयन के अंतरंग ज्ञान के बिना कोई भी इस समारोह को सही ढंग से लागू नहीं कर सकता है:

float roundf(x)
{
  int signbit;
  __uint32_t w;
  /* Most significant word, least significant word. */
  int exponent_less_127;

  GET_FLOAT_WORD(w, x);

  /* Extract sign bit. */
  signbit = w & 0x80000000;

  /* Extract exponent field. */
  exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;

  if (exponent_less_127 < 23)
    {
      if (exponent_less_127 < 0)
        {
          w &= 0x80000000;
          if (exponent_less_127 == -1)
            /* Result is +1.0 or -1.0. */
            w |= ((__uint32_t)127 << 23);
        }
      else
        {
          unsigned int exponent_mask = 0x007fffff >> exponent_less_127;
          if ((w & exponent_mask) == 0)
            /* x has an integral value. */
            return x;

          w += 0x00400000 >> exponent_less_127;
          w &= ~exponent_mask;
        }
    }
  else
    {
      if (exponent_less_127 == 128)
        /* x is NaN or infinite. */
        return x + x;
      else
        return x;
    }
  SET_FLOAT_WORD(x, w);
  return x;
}

दूसरी ओर, यदि कोई अन्य समाधान प्रयोग करने योग्य नहीं है तो newlib संभावित रूप से एक विकल्प हो सकता है क्योंकि यह एक अच्छी तरह से परीक्षण किया गया कार्यान्वयन है।


5
@downvoter कृपया बताएं कि क्या सुधार किया जा सकता है? यहाँ उत्तर का अधिकांश हिस्सा सिर्फ इसलिए गलत है क्योंकि वे अपना राउंड रोल करने का प्रयास करते हैं जो सभी एक या दूसरे रूप में विफल हो जाते हैं। अगर मेरे स्पष्टीकरण में कुछ कमी है तो कृपया मुझे बताएं।
शफिक याघमोर

1
अच्छा पूर्ण उत्तर - विशेष रूप से सिर्फ 0.5 भाग के नीचे। एक और आला round(-0.0):। सी कल्पना निर्दिष्ट करने के लिए प्रकट नहीं होता है। मैं -0.0एक परिणाम के रूप में की उम्मीद है ।
chux -

3
@chux रुचिकर, और IEEE 754-2008 मानक निर्दिष्ट करता है कि गोलाई ज़ीरो और इनफिनिटी के संकेतों को बनाए रखता है (देखें 5.9)।
रुस्लान

1
@ शफीक यह एक शानदार जवाब है। मैंने कभी नहीं सोचा था कि गोलाई एक गैर तुच्छ ऑपरेशन है।
रुस्लान

1
शायद यह ध्यान देने योग्य है कि संख्यात्मक और प्रदर्शन कारणों से C ++ 11 उपलब्ध होने पर std::rint()अक्सर बेहतर std::round()होता है। यह मौजूदा राउंडिंग मोड का उपयोग करता है, round()विशेष मोड के विपरीत । यह x86 पर बहुत अधिक कुशल हो सकता है, जहां rintएक निर्देश के लिए इनलाइन हो सकता है। (gcc और -ffast-math clang बिना godbolt.org/g/5UsL2e के भी ऐसा करते हैं, जबकि केवल क्लैंग लगभग समान है nearbyint()) ARM के लिए सिंगल-इंस्ट्रक्शन सपोर्ट है round(), लेकिन x86 पर यह केवल कई निर्देशों के साथ इनलाइन कर सकता है, और केवल-ffast-math
पीटर कॉर्ड्स के साथ

71

यह ध्यान देने योग्य हो सकता है कि यदि आप गोलाई से पूर्णांक परिणाम चाहते थे तो आपको इसे छत या फर्श के माध्यम से पारित करने की आवश्यकता नहीं है। अर्थात,

int round_int( double r ) {
    return (r > 0.0) ? (r + 0.5) : (r - 0.5); 
}

3
0.49999999999999994 के लिए अपेक्षित परिणाम नहीं देता है (हालांकि, इस बात पर निर्भर करता है कि आप क्या उम्मीद करते हैं, लेकिन 0 मेरे लिए 1 से अधिक उचित लगता है)
stijn

@stijn अच्छा कैच। मैंने पाया कि मेरे स्थिरांक में लंबे डबल शाब्दिक प्रत्यय को जोड़ने से आपका उदाहरण मुद्दा तय होता है, लेकिन मुझे नहीं पता कि क्या अन्य सटीक उदाहरण हैं जो इसे पकड़ नहीं पाएंगे।
21

1
btw अगर आप 0.5 के बजाय 0.49999999999999994 जोड़ते हैं, तो यह इनपुट के रूप में 0.499999999999994 और 5000000000000001.0 दोनों के लिए ठीक काम करता है। यह निश्चित नहीं है कि क्या यह सभी मूल्यों के लिए ठीक है, और मुझे यह बताते हुए कोई संदर्भ नहीं मिला कि यह अंतिम फिक्स है।
12

1
@stijn यह सभी मूल्यों के लिए ठीक है, अगर आप इस बात की परवाह नहीं करते हैं कि दो पूर्णांकों के बीच के मान किस दिशा में गोल हैं। बिना सोचे समझे, मैं इसे निम्नलिखित मामलों के साथ केस विश्लेषण द्वारा साबित करूंगा: 0 <= d <0.5, 0.5 <= d <1.5, 1.5 <= d <2 ^ 52, d> = 2 ^ 52। मैंने एकल-सटीक मामले का भी सहजता से परीक्षण किया।
पास्कल कुओक

3
प्रति 4.9 [conv.fpint], "यदि गंतव्य स्थान प्रकार में काटे गए मान का प्रतिनिधित्व नहीं किया जा सकता है तो व्यवहार अपरिभाषित है।" , तो यह थोड़ा खतरनाक है। अन्य एसओ जवाब देते हैं कि यह कैसे मजबूत किया जाए।
टोनी डेलारॉय

41

यह cmath में C ++ 11 के बाद से उपलब्ध है ( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf के अनुसार )

#include <cmath>
#include <iostream>

int main(int argc, char** argv) {
  std::cout << "round(0.5):\t" << round(0.5) << std::endl;
  std::cout << "round(-0.5):\t" << round(-0.5) << std::endl;
  std::cout << "round(1.4):\t" << round(1.4) << std::endl;
  std::cout << "round(-1.4):\t" << round(-1.4) << std::endl;
  std::cout << "round(1.6):\t" << round(1.6) << std::endl;
  std::cout << "round(-1.6):\t" << round(-1.6) << std::endl;
  return 0;
}

आउटपुट:

round(0.5):  1
round(-0.5): -1
round(1.4):  1
round(-1.4): -1
round(1.6):  2
round(-1.6): -2

1
वहाँ भी है lroundऔर llroundअभिन्न परिणाम के लिए
sp2danny

@ sp2danny: या इससे बेहतर, lrintमौजूदा फेरीिंग मोड का उपयोग करने के बजाय फ़िरकी round-से-शून्य टाईब्रेक।
पीटर कॉर्डेस

27

यह आमतौर पर के रूप में लागू किया जाता है floor(value + 0.5)

संपादित करें: और यह संभवत: गोल नहीं है क्योंकि मुझे पता है कि कम से कम तीन राउंडिंग एल्गोरिदम हैं: गोल से शून्य, गोल से निकटतम पूर्णांक और बैंकर की गोलाई। आप निकटतम पूर्णांक के लिए दौर पूछ रहे हैं।


1
'दौर' के विभिन्न संस्करणों के बीच अंतर करना अच्छा है। यह जानकर अच्छा लगता है कि कब किसे चुनें।
xtofl

5
वास्तव में अलग-अलग राउंडिंग एल्गोरिदम हैं जो सभी "सही" होने का उचित दावा कर सकते हैं। हालाँकि मंजिल (मान + 0.5) इनमें से एक नहीं है। कुछ मानों के लिए, जैसे कि 0.49999997f या समकक्ष डबल, उत्तर सिर्फ गलत है - यह 1.0 तक होगा जब सभी सहमत होंगे कि यह शून्य होना चाहिए। विवरण के लिए इस पोस्ट को देखें: blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1
ब्रूस डॉसन

14

2 समस्याएं हैं जिन्हें हम देख रहे हैं:

  1. गोल रूपांतरण
  2. प्रकार रूपांतरण।

गोलाई रूपांतरण का अर्थ है गोलाई round फ्लोट / डबल निकटतम निकटतम मंजिल / छत फ्लोट / डबल। हो सकता है आपकी समस्या यहीं खत्म हो जाए। लेकिन अगर आपसे Int / Long लौटने की उम्मीद की जाती है, तो आपको टाइप रूपांतरण करने की आवश्यकता है, और इस प्रकार "ओवरफ्लो" समस्या आपके समाधान को प्रभावित कर सकती है। एसओ, अपने फ़ंक्शन में त्रुटि की जांच करें

long round(double x) {
   assert(x >= LONG_MIN-0.5);
   assert(x <= LONG_MAX+0.5);
   if (x >= 0)
      return (long) (x+0.5);
   return (long) (x-0.5);
}

#define round(x) ((x) < LONG_MIN-0.5 || (x) > LONG_MAX+0.5 ?\
      error() : ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

से: http://www.cs.tut.fi/~jkorpela/round.html


जटिलताओं का उपयोग करना LONG_MIN-0.5और LONG_MAX+0.5उनका परिचय देना क्योंकि गणित सटीक नहीं हो सकता है। सटीक रूपांतरण के लिए सटीक LONG_MAXसे अधिक हो सकता है double। आगे की संभावना चाहते हैं assert(x < LONG_MAX+0.5); (<बनाम <=) के रूप में LONG_MAX+0.5बिल्कुल प्रतिनिधित्व किया जा सकता है और (x)+0.5सटीक परिणाम हो सकता है LONG_MAX+1जिसमें से एक longकास्ट विफल रहता है । अन्य कोने के मुद्दे भी।
चक्स -

अपने फ़ंक्शन को कॉल न करें round(double), पहले से ही उस नाम का एक मानक गणित लाइब्रेरी फ़ंक्शन है (C ++ 11 में) इसलिए यह भ्रमित है। std::lrint(x)यदि यह उपलब्ध हो तो उपयोग करें ।
पीटर कॉर्डेस

11

बूस्ट में एक निश्चित प्रकार की गोलाई भी लागू की जाती है:

#include <iostream>

#include <boost/numeric/conversion/converter.hpp>

template<typename T, typename S> T round2(const S& x) {
  typedef boost::numeric::conversion_traits<T, S> Traits;
  typedef boost::numeric::def_overflow_handler OverflowHandler;
  typedef boost::numeric::RoundEven<typename Traits::source_type> Rounder;
  typedef boost::numeric::converter<T, S, Traits, OverflowHandler, Rounder> Converter;
  return Converter::convert(x);
}

int main() {
  std::cout << round2<int, double>(0.1) << ' ' << round2<int, double>(-0.1) << ' ' << round2<int, double>(-0.9) << std::endl;
}

ध्यान दें कि यह तभी काम करता है जब आप एक पूर्णांक रूपांतरण करते हैं।


2
बूस्ट सरल गोलाई कार्यों का एक सेट भी प्रदान करता है; मेरा जवाब देखिए।
डैनियल वुल्फ

boost:numeric::RoundEven< double >::nearbyintयदि आप पूर्णांक नहीं चाहते हैं, तो आप सीधे भी उपयोग कर सकते हैं । @ डैनियलवॉल्फ़ ध्यान दें कि सरल फ़ंक्शन +0.5 का उपयोग करके कार्यान्वित किया जाता है, जिसमें aka.nice द्वारा बिछाने के रूप में समस्याएं हैं
stijn

6

आप निम्न के साथ n अंकों के लिए गोल कर सकते हैं:

double round( double x )
{
const double sd = 1000; //for accuracy to 3 decimal places
return int(x*sd + (x<0? -0.5 : 0.5))/sd;
}

4
जब तक आपके कंपाइलर इंट साइज़ की चूक 1024 बिट्स तक नहीं हो जाती, यह विशाल डबल के लिए सटीक नहीं होगा ...
aka.nice

मुझे लगता है कि जब इसका उपयोग किया जाएगा तो यह स्वीकार्य है: यदि आपका डबल मान 1.0 e + 19 है, तो 3 स्थानों पर चक्कर लगाना कोई मतलब नहीं है।
कार्ल

3
यकीन है, लेकिन सवाल एक सामान्य दौर के लिए है, और आप इसे नियंत्रित नहीं कर सकते कि इसका उपयोग कैसे किया जाएगा। ऐसे दौर में असफल होने का कोई कारण नहीं है जहां छत और फर्श नहीं होंगे।
उर्फ.निसे

यह सीमा के बाहर आर्ग्स के लिए अपरिभाषित व्यवहार है int। (X86 पर प्रैक्टिस में, आउट-ऑफ- द -रेंज एफपी मान पूर्णांक बिट पैटर्न के रूप में उत्पादन करेगाCVTTSD2SI0x80000000 , अर्थात INT_MIN, जिसे बाद में वापस बदल दिया जाएगा double
पीटर कॉर्ड्स

5

इन दिनों C ++ 11 संकलक का उपयोग करने की समस्या नहीं होनी चाहिए जिसमें C99 / C ++ 11 गणित पुस्तकालय शामिल है। लेकिन फिर सवाल यह हो जाता है: आप कौन सा गोल फंक्शन चुनते हैं?

C99 / C ++ 11 round()वास्तव में अक्सर वह राउंडिंग फ़ंक्शन नहीं है जो आप चाहते हैं । यह एक फंकी राउंडिंग मोड का उपयोग करता है जो आधे रास्ते के मामलों ( +-xxx.5000) पर टाई-ब्रेक के रूप में 0 से दूर होता है । यदि आप विशेष रूप से उस राउंडिंग मोड को चाहते हैं, या आप एक C ++ कार्यान्वयन को लक्षित कर रहे हैं, जहां round()की तुलना में तेज़ है rint(), तो इसका उपयोग करें (या इस प्रश्न पर अन्य उत्तरों में से एक के साथ अपने व्यवहार का अनुकरण करें जो इसे अंकित मूल्य पर ले गया और ध्यान से पुन: प्रस्तुत किया गया कि विशिष्ट गोल व्यवहार)

round()टाई-ब्रेक के साथ भी IEEE754 डिफ़ॉल्ट राउंड निकटतम मोड से अलग है । निकटतम-सम संख्या के औसत परिमाण में सांख्यिकीय पूर्वाग्रह से बचता है, लेकिन संख्याओं के प्रति भी पूर्वाग्रह करता है।

दो गणित पुस्तकालय गोलाई कार्य हैं जो वर्तमान डिफ़ॉल्ट गोलाई मोड का उपयोग करते हैं: std::nearbyint()और std::rint(), दोनों C99 / C ++ 11 में जोड़े गए हैं, इसलिए वे किसी भी समय उपलब्ध हैं std::round()। फर्क सिर्फ इतना है कि nearbyintFE_INEXACT को कभी नहीं बढ़ाता।

rint()प्रदर्शन कारणों के लिए पसंद करें : जीसीसी और क्लैंग दोनों इसे अधिक आसानी से इनलाइन करते हैं, लेकिन जीसीसी कभी nearbyint()भी इनलाइन नहीं होता (यहां तक ​​कि -ffast-math)


x86-64 और AArch64 के लिए gcc / clang

मैंने मैट गॉडबोल्ट के कंपाइलर एक्सप्लोरर पर कुछ परीक्षण कार्य किए , जहाँ आप स्रोत (asm आउटपुट) (कई कंपाइलरों के लिए) देख सकते हैं। कंपाइलर आउटपुट पढ़ने के बारे में अधिक जानने के लिए, इस प्रश्नोत्तर को देखें , और मैट के CppCon2017 में बात करें: “मेरे साथी ने मेरे लिए क्या किया है? संकलक के ढक्कन को चिह्नित करना ,

FP कोड में, यह आमतौर पर छोटे कार्यों को इनलाइन करने के लिए एक बड़ी जीत है। विशेष रूप से गैर-विंडोज पर, जहां मानक कॉलिंग कन्वेंशन में कॉल-संरक्षित रजिस्टर नहीं हैं, इसलिए कंपाइलर एक्सएमएम रजिस्टरों में किसी भी एफपी मूल्यों को नहीं रख सकता है call। तो भले ही आप वास्तव में asm को नहीं जानते हों, फिर भी आप आसानी से देख सकते हैं कि क्या यह केवल लाइब्रेरी फ़ंक्शन के लिए एक टेल-कॉल है या क्या यह एक या दो गणित निर्देशों के लिए है। जो कुछ भी एक या दो निर्देशों के लिए आता है, वह फ़ंक्शन कॉल (x86 या ARM पर इस विशेष कार्य के लिए) से बेहतर है।

X86 पर, SSE4.1 के लिए जो कुछ भी है वह roundsdSSE4.1 roundpd(या AVX vroundpd) के साथ ऑटो- वेक्टर कर सकता है । (FP-> पूर्णांक रूपांतरण भी पैक किए गए SIMD फॉर्म में उपलब्ध हैं, केवल FP-> 64-बिट पूर्णांक के लिए जिसे AVX512 की आवश्यकता होती है।

  • std::nearbyint():

    • x86 क्लैंग: एक इंस के साथ इनलाइन -msse4.1
    • x86 gcc: केवल -msse4.1 -ffast-mathऔर केवल g 5.4 और पहले के संस्करण के साथ एक ही इन्स के लिए इनलाइन । बाद में जीसीसी ने इसे कभी भी अस्वीकार नहीं किया (हो सकता है कि उन्हें महसूस नहीं हुआ था कि तत्काल बिट्स में से एक अनुभवहीन अपवाद को दबा सकता है; जो कि क्लैंग का उपयोग करता है, लेकिन पुराने जीसीसी उसी समय का तत्काल उपयोग करता है rintजब वह इनलाइन करता है)
    • AArch64 gcc6.3: डिफ़ॉल्ट रूप से एकल इंस के लिए इनलाइन।
  • std::rint:

    • x86 क्लैंग: एक इंस के साथ इनलाइन -msse4.1
    • x86 gcc7: एक इंस के साथ इनलाइन -msse4.1। (SSE4.1 के बिना, कई निर्देशों के अनुसार)
    • x86 gcc6.x और पहले: एक इंस के साथ इनलाइन -ffast-math -msse4.1
    • AArch64 gcc: डिफ़ॉल्ट रूप से एक इंस के लिए इनलाइन
  • std::round:

    • x86 क्लैंग: इनलाइन नहीं है
    • x86 gcc: -ffast-math -msse4.1दो वेक्टर स्थिरांक की आवश्यकता के साथ कई निर्देशों के लिए इनलाइन ।
    • AArch64 gcc: एक एकल निर्देश (इस राउंडिंग मोड के लिए HW सपोर्ट के साथ-साथ IEEE डिफॉल्ट और अधिकांश अन्य के लिए इनलाइन)।
  • std::floor/ std::ceil/std::trunc

    • x86 क्लैंग: एक इंस के साथ इनलाइन -msse4.1
    • x86 gcc7.x: एक इंस के साथ इनलाइन -msse4.1
    • x86 gcc6.x और पहले: एक इंस के साथ इनलाइन -ffast-math -msse4.1
    • AArch64 gcc: एक निर्देश के लिए डिफ़ॉल्ट रूप से इनलाइन

int/ long/ long long: के लिए गोलाई

आपके पास यहां दो विकल्प हैं: उपयोग lrint(जैसे rintलेकिन रिटर्न long, या के long longलिए llrint), या एफपी-> एफपी राउंडिंग फ़ंक्शन का उपयोग करें और फिर एक पूर्णांक में परिवर्तित करें सामान्य तरीके (ट्रंकेशन के साथ)। कुछ कंपाइलर एक तरह से दूसरे से बेहतर तरीके से अनुकूलन करते हैं।

long l = lrint(x);

int  i = (int)rint(x);

ध्यान दें कि int i = lrint(x)धर्मान्तरित floatया double-> longपहले, और फिर पूर्णांक को काटता है int। यह आउट-ऑफ-रेंज रेंजर्स के लिए एक अंतर बनाता है: C ++ में अपरिभाषित व्यवहार, लेकिन x86 FP के लिए अच्छी तरह से परिभाषित -> int निर्देश (जो संकलक तब तक उत्सर्जित करेगा जब तक कि यह निरंतर प्रसार के दौरान संकलन समय पर यूबी नहीं देखता है, तो यह है कोड बनाने की अनुमति देता है जो टूट जाता है यदि इसे कभी भी निष्पादित किया जाता है)।

X86 पर, एक FP-> पूर्णांक रूपांतरण जो पूर्णांक को उत्पन्न करता है INT_MINया LLONG_MIN( 0x8000000केवल साइन-बिट सेट के साथ या 64-बिट समतुल्य का एक-प्रतिमान )। इंटेल इसे "पूर्णांक अनिश्चितकालीन" मान कहता है। (देखें मैनुअल प्रविष्टि , SSE2 अनुदेश कि धर्मान्तरित (काट-छांट के साथ) पर हस्ताक्षर किए पूर्णांक तक अदिश डबल। इसके साथ उपलब्ध है 32-बिट या 64-बिट पूर्णांक गंतव्य (केवल 64-बिट मोड में)। वहाँ भी है एक (वर्तमान राउंडिंग के साथ परिवर्तित मोड), जिसे हम संकलक को उत्सर्जित करना चाहते हैं, लेकिन दुर्भाग्य से gcc और clang ऐसा नहीं करेंगे ।cvttsd2sicvtsd2si-ffast-math

यह भी सावधान रहें कि unsignedx86 पर (AVX512 के बिना) एफपी / से इंट / लॉन्ग कम कुशल है। 64-बिट मशीन पर 32-बिट अहस्ताक्षरित रूपांतरण बहुत सस्ता है; बस 64-बिट पर हस्ताक्षर किए और काट-छाँट में परिवर्तित करें। लेकिन अन्यथा यह काफी धीमा है।

  • x86 के साथ / बिना -ffast-math -msse4.1: (int/long)rintinlines के roundsd/ के साथ cvttsd2si। (अनुकूलन को याद किया cvtsd2si)। lrintइनलाइन बिलकुल नहीं है।

  • x86 gcc6.x और पहले बिना -ffast-math: न तो इनलाइन

  • x86 gcc7 बिना -ffast-math: (int/long)rintराउंड और अलग-अलग रूपांतरित (SSE4.1 के 2 कुल निर्देशों के साथ सक्षम है, अन्यथा rintबिना कोड के एक झुंड के साथ roundsd)। lrintइनलाइन नहीं है।
  • x86 gcc के साथ -ffast-math : सभी तरीके इनलाइन cvtsd2si(इष्टतम) , SSE4.1 की कोई आवश्यकता नहीं।

  • AAr6464 gcc6.3 बिना -ffast-math: (int/long)rint2 निर्देशों के लिए इनलाइन । lrintइनलाइन नहीं है

  • AArch64 gcc6.3 -ffast-math: के साथ (int/long)rintकॉल करने के लिए संकलित करता है lrintlrintइनलाइन नहीं है। यह एक गलत अनुकूलन हो सकता है जब तक कि बिना दो निर्देश हमें -ffast-mathबहुत धीमे नहीं मिलते ।

TODO: ICC और MSVC भी Godbolt पर उपलब्ध हैं, लेकिन मैंने इसके लिए उनके आउटपुट को नहीं देखा है। आपका स्वागत है ... इसके अलावा: क्या यह संकलक / संस्करण द्वारा पहले और फिर उसके भीतर कार्य द्वारा टूटना अधिक उपयोगी होगा? अधिकांश लोग एफपी-> एफपी या एफपी-> पूर्णांक गोलाई को संकलित करने के आधार पर कंपाइलरों को स्विच करने नहीं जा रहे हैं।
पीटर कॉर्डेस

2
+1 यह अनुशंसा करने के लिए कि rint()जहां एक संभव विकल्प है, जो आमतौर पर मामला है। मुझे लगता है कि नाम round()कुछ प्रोग्रामर से तात्पर्य है कि यह वही है जो वे चाहते हैं, जबकि rint()रहस्यमय लगता है। ध्यान दें कि round()"फंकी" राउंडिंग मोड का उपयोग नहीं करता है: राउंड-टू-निकटतम-संबंधों-दूर एक आधिकारिक IEEE-754 (2008) राउंडिंग मोड है। यह उत्सुक है कि nearbyint()इनलेट नहीं मिलता है, यह देखते हुए कि यह काफी हद तक समान है rint(), और शर्तों के तहत समान होना चाहिए -ffast-math। वह मुझे बग-ईश लगता है।
njuffa

4

से सावधान रहें floor(x+0.5)। यहाँ विषम संख्या के लिए क्या हो सकता है [2 ^ 52,2 ^ 53]:

-bash-3.2$ cat >test-round.c <<END

#include <math.h>
#include <stdio.h>

int main() {
    double x=5000000000000001.0;
    double y=round(x);
    double z=floor(x+0.5);
    printf("      x     =%f\n",x);
    printf("round(x)    =%f\n",y);
    printf("floor(x+0.5)=%f\n",z);
    return 0;
}
END

-bash-3.2$ gcc test-round.c
-bash-3.2$ ./a.out
      x     =5000000000000001.000000
round(x)    =5000000000000001.000000
floor(x+0.5)=5000000000000002.000000

यह http://bugs.squeak.org/view.php?id=7134 है । @Konik की तरह एक समाधान का उपयोग करें।

मेरा अपना मजबूत संस्करण कुछ इस तरह होगा:

double round(double x)
{
    double truncated,roundedFraction;
    double fraction = modf(x, &truncated);
    modf(2.0*fraction, &roundedFraction);
    return truncated + roundedFraction;
}

फर्श से बचने का एक और कारण (x + 0.5) यहां दिया गया है


2
मैं डाउनवोट्स के बारे में जानना चाहता हूं। क्या ऐसा इसलिए है क्योंकि टाई को निकटतम से दूर करने के बजाय शून्य से दूर हल किया जाता है?
उर्फ़.नाइस

1
नोट: सी कल्पना कहती है, "वर्तमान दौर की दिशा की परवाह किए बिना, आधे रास्ते के मामलों को शून्य से दूर करना।"
चक्स -

4

यदि आप अंततः doubleअपने round()फ़ंक्शन के आउटपुट को a में बदलना चाहते हैं int, तो इस प्रश्न के स्वीकृत समाधान कुछ इस तरह दिखाई देंगे:

int roundint(double r) {
  return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5));
}

यह मेरी मशीन पर लगभग 8.88 एनएस की घड़ियां जब समान रूप से यादृच्छिक मूल्यों में पारित हो जाता है।

नीचे कार्यात्मक रूप से समतुल्य है, जहां तक ​​मैं बता सकता हूं, लेकिन एक महत्वपूर्ण प्रदर्शन के लिए मेरी मशीन पर 2.48 एनएस में घड़ियां :

int roundint (double r) {
  int tmp = static_cast<int> (r);
  tmp += (r-tmp>=.5) - (r-tmp<=-.5);
  return tmp;
}

बेहतर प्रदर्शन के कारणों में से एक है स्किपिंग ब्रांचिंग।


यह सीमा के बाहर आर्ग्स के लिए अपरिभाषित व्यवहार है int। (X86 पर प्रैक्टिस में, आउट-ऑफ- द -रेंज एफपी मान पूर्णांक बिट पैटर्न के रूप में उत्पादन करेगाCVTTSD2SI0x80000000 , अर्थात INT_MIN, जिसे बाद में वापस बदल दिया जाएगा double
पीटर कॉर्ड्स

2

कुछ भी लागू करने की कोई आवश्यकता नहीं है, इसलिए मुझे यकीन नहीं है कि इतने सारे उत्तर में परिभाषित, कार्य या तरीके शामिल हैं।

C99 में

टाइप-जेनेरिक मैक्रोज़ के लिए हमारे पास निम्नलिखित और हेडर <tgmath.h> हैं।

#include <math.h>
double round (double x);
float roundf (float x);
long double roundl (long double x);

यदि आप इसे संकलित नहीं कर सकते हैं, तो संभवतः आपने गणित पुस्तकालय छोड़ दिया है। इसके समान एक कमांड मेरे द्वारा (C) हर C कंपाइलर पर काम करता है।

gcc -lm -std=c99 ...

C ++ 11 में

हमारे पास #include <cmath> में निम्नलिखित और अतिरिक्त अधिभार हैं जो IEEE डबल सटीक फ़्लोटिंग पॉइंट पर निर्भर करते हैं।

#include <math.h>
double round (double x);
float round (float x);
long double round (long double x);
double round (T x);

हैं एसटीडी नाम स्थान में समकक्ष भी।

यदि आप इसे संकलित नहीं कर सकते हैं, तो आप C ++ के बजाय C संकलन का उपयोग कर सकते हैं। निम्न मूल आदेश g ++ 6.3.1, x86_64-w64-mingw32-g ++ 6.3.0, clang-x86_64 ++ 3.8.0, और Visual C ++ 2015 समुदाय के साथ न तो त्रुटि और न ही चेतावनी देता है।

g++ -std=c++11 -Wall

ऑर्डिनल डिवीजन के साथ

दो क्रमिक संख्याओं को विभाजित करते समय, जहां T छोटा है, int, long, या अन्य ordinal, राउंडिंग एक्सप्रेशन है।

T roundedQuotient = (2 * integerNumerator + 1)
    / (2 * integerDenominator);

शुद्धता

इसमें कोई संदेह नहीं है कि फ्लोटिंग पॉइंट ऑपरेशंस में विषम दिखने वाली अशुद्धियाँ दिखाई देती हैं, लेकिन यह केवल तब होता है जब संख्याएँ दिखाई देती हैं, और गोलाई के साथ बहुत कम होता है।

स्रोत आईईईई के फ्लोटिंग पॉइंट नंबर के मंटिसा में केवल महत्वपूर्ण अंकों की संख्या नहीं है, यह मनुष्यों के रूप में हमारी दशमलव सोच से संबंधित है।

टेन पाँच और दो का उत्पाद है, और 5 और 2 अपेक्षाकृत प्रमुख हैं। इसलिए IEEE फ़्लोटिंग पॉइंट मानकों को संभवतः सभी बाइनरी डिजिटल अभ्यावेदन के लिए दशमलव संख्या के रूप में पूरी तरह से प्रतिनिधित्व नहीं किया जा सकता है।

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


1
"मुझे यकीन नहीं है कि इतने सारे उत्तर में परिभाषित, कार्य या तरीके शामिल हैं।" जब यह पूछा गया था पर एक नज़र है - C ++ 11 अभी तक बाहर नहीं था। ;)
जेग्डस्पाइरे

@jaggedSpire, अच्छी तरह से मुझे एक अंगूठा दे दो, अगर आपको लगता है कि यह उचित है, क्योंकि सभी उच्च स्कोरिंग उत्तर आज के सबसे अधिक इस्तेमाल किए जाने वाले संकलक के संदर्भ में अप्रचलित और भ्रामक हैं।
फौशरथियन

2

फ़ंक्शन double round(double)के उपयोग के साथ modfकार्य:

double round(double x)
{
    using namespace std;

    if ((numeric_limits<double>::max() - 0.5) <= x)
        return numeric_limits<double>::max();

    if ((-1*std::numeric_limits<double>::max() + 0.5) > x)
        return (-1*std::numeric_limits<double>::max());

    double intpart;
    double fractpart = modf(x, &intpart);

    if (fractpart >= 0.5)
        return (intpart + 1);
    else if (fractpart >= -0.5)
        return intpart;
    else
        return (intpart - 1);
    }

स्वच्छ संकलित होने के लिए, "math.h" और "सीमाएं" आवश्यक हैं। फ़ंक्शन निम्नलिखित गोलाई स्कीमा के अनुसार काम करता है:

  • राउंड ऑफ़ 5.0 है
  • 3.8 का दौर 4.0 है
  • 2.3 का राउंड 2.0 है
  • 1.5 का राउंड 2.0 है
  • 0.501 का राउंड 1.0 है
  • 0.5 का राउंड 1.0 है
  • 0.499 का राउंड 0.0 है
  • 0.01 का राउंड 0.0 है
  • राउंड ऑफ़ 0.0 है
  • -0.01 का राउंड -0.0 है
  • -0.499 का राउंड -0.0 है
  • -0.5 का राउंड -0.0 है
  • -0.501 का राउंड -1.0 है
  • -1.5 का राउंड -1.0 है
  • -2.3 का दौर -2.0 है
  • -3.8 का राउंड -4.0 है
  • -5.0 का राउंड -5.0 है

2
यह एक अच्छा उपाय है। मुझे यकीन नहीं है कि -1.5 से -1.0 राउंडिंग मानक है, फिर भी मैं सहानुभूति से -2.0 की उम्मीद करूंगा। इसके अलावा, मैं प्रमुख गार्ड की बात नहीं देखता, पहले दो को अगर हटाया जा सकता है।
उर्फ.निसे

2
मैंने ISO / IEC 10967-2 मानक, ओपन-std.org/jtc1/sc22/wg11/docs/n462.pdf में और परिशिष्ट B.5.2.4 से जाँच की, राउंडिंग फंक्शन वास्तव में सममित होना चाहिए, rounding_F (x) = neg_F (गोलाई_एफ (neg_F (x)))
aka.nice

यह C ++ 11 rint()या की तुलना में धीमा होने वाला है nearbyint(), लेकिन अगर आप वास्तव में एक कंपाइलर का उपयोग नहीं कर सकते हैं जो एक उचित राउंडिंग फ़ंक्शन प्रदान करता है, और आपको प्रदर्शन से अधिक सटीक होने की आवश्यकता है ...
पीटर कॉर्डेस

1

यदि आपको C ++ 11 मानक का समर्थन करने वाले वातावरण में कोड संकलित करने की आवश्यकता है, लेकिन वातावरण में समान कोड को संकलित करने में भी सक्षम होने की आवश्यकता है, तो आप std के बीच चयन करने के लिए फ़ंक्शन मैक्रो का उपयोग कर सकते हैं :: दौर () और प्रत्येक प्रणाली के लिए एक कस्टम फ़ंक्शन। बस पास -DCPP11या /DCPP11C ++ 11-अनुरूप कंपाइलर (या इसके अंतर्निहित संस्करण मैक्रोज़ का उपयोग करें), और एक हेडर बनाएं जैसे:

// File: rounding.h
#include <cmath>

#ifdef CPP11
    #define ROUND(x) std::round(x)
#else    /* CPP11 */
    inline double myRound(double x) {
        return (x >= 0.0 ? std::floor(x + 0.5) : std::ceil(x - 0.5));
    }

    #define ROUND(x) myRound(x)
#endif   /* CPP11 */

एक त्वरित उदाहरण के लिए, http://ideone.com/zal709 देखें

यह उन परिवेशों में std :: round () का अनुमान लगाता है जो -0.0 के लिए साइन बिट के संरक्षण सहित C ++ 11-अनुरूप नहीं हैं। यह एक मामूली प्रदर्शन का कारण हो सकता है, हालांकि, और संभवतः कुछ ज्ञात "समस्या" को फ़्लोटिंग-पॉइंट मान जैसे कि 0.49999999999999994 या इसी तरह के मान के साथ समस्याएँ हैं।

वैकल्पिक रूप से, यदि आपके पास C ++ 11-संगत कंपाइलर तक पहुंच है, तो आप अपने <cmath>हेडर से सिर्फ std :: दौर () को पकड़ सकते हैं , और इसका उपयोग अपने स्वयं के हेडर बनाने के लिए कर सकते हैं जो फ़ंक्शन को परिभाषित करता है यदि यह पहले से परिभाषित नहीं है। ध्यान दें कि यह एक इष्टतम समाधान नहीं हो सकता है, हालांकि, खासकर यदि आपको कई प्लेटफार्मों के लिए संकलन करने की आवश्यकता है।


1

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

    // round a floating point number to the nearest integer
    template <typename Arg>
    int Round(Arg arg)
    {
#ifndef NDEBUG
        // check that the argument can be rounded given the return type:
        if (
            (Arg)std::numeric_limits<int>::max() < arg + (Arg) 0.5) ||
            (Arg)std::numeric_limits<int>::lowest() > arg - (Arg) 0.5)
            )
        {
            throw std::overflow_error("out of bounds");
        }
#endif

        return (arg > (Arg) 0.0) ? (int)(r + (Arg) 0.5) : (int)(r - (Arg) 0.5);
    }

1
जैसा कि मैंने अपने जवाब में कहा 0.5है कि सभी मामलों में काम नहीं करता है। हालांकि कम से कम आप अतिप्रवाह मुद्दे से निपटते हैं ताकि आप अपरिभाषित व्यवहार से बचें।
शफिक याघमोर

1

जैसा कि टिप्पणियों और अन्य उत्तरों में बताया गया है, आईएसओ सी ++ मानक पुस्तकालय ने round()आईएसओ सी ++ 11 तक जोड़ा नहीं था , जब इस समारोह को आईएसओ सी 99 मानक गणित पुस्तकालय के संदर्भ में खींचा गया था।

[½, ub ] में सकारात्मक संचालन के लिए round(x) == floor (x + 0.5), जहां ub को IEEE-754 (2008) के लिए मैप करने के लिए 2 23 है , और floatIEEE-754 (2008) के लिए मैप किए जाने पर binary322 52 के लिए । संख्या 23 और 52 इन दो फ्लोटिंग-पॉइंट स्वरूपों में संग्रहीत मंटिसा बिट्स की संख्या के अनुरूप हैं । [+0, ½) में सकारात्मक ऑपरेंड के लिए, और ( ub , + is ) में सकारात्मक ऑपरेंड के लिए । जैसा कि फ़ंक्शन एक्स-एक्सिस के बारे में सममित है, नकारात्मक तर्क के अनुसार नियंत्रित किया जा सकता है ।doublebinary64round(x) == 0round(x) == xxround(-x) == -round(x)

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

मैंने Intel संकलक संस्करण 13 का उपयोग करते हुए, दोनों के साथ my_roundf()newlib roundf()कार्यान्वयन के खिलाफ बहुत ही सहजता से परीक्षण किया /fp:strictऔर /fp:fast। मैंने यह भी जाँच लिया कि newlib संस्करण Intel संकलक roundf()के mathimfपुस्तकालय में मेल खाता है । डबल-परिशुद्धता के लिए थकावट का परीक्षण संभव नहीं है round(), हालांकि कोड एकल-सटीक कार्यान्वयन के लिए संरचनात्मक रूप से समान है।

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>

float my_roundf (float x)
{
    const float half = 0.5f;
    const float one = 2 * half;
    const float lbound = half;
    const float ubound = 1L << 23;
    float a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floorf (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

double my_round (double x)
{
    const double half = 0.5;
    const double one = 2 * half;
    const double lbound = half;
    const double ubound = 1ULL << 52;
    double a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floor (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

uint32_t float_as_uint (float a)
{
    uint32_t r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float uint_as_float (uint32_t a)
{
    float r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float newlib_roundf (float x)
{
    uint32_t w;
    int exponent_less_127;

    w = float_as_uint(x);
    /* Extract exponent field. */
    exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;
    if (exponent_less_127 < 23) {
        if (exponent_less_127 < 0) {
            /* Extract sign bit. */
            w &= 0x80000000;
            if (exponent_less_127 == -1) {
                /* Result is +1.0 or -1.0. */
                w |= ((uint32_t)127 << 23);
            }
        } else {
            uint32_t exponent_mask = 0x007fffff >> exponent_less_127;
            if ((w & exponent_mask) == 0) {
                /* x has an integral value. */
                return x;
            }
            w += 0x00400000 >> exponent_less_127;
            w &= ~exponent_mask;
        }
    } else {
        if (exponent_less_127 == 128) {
            /* x is NaN or infinite so raise FE_INVALID by adding */
            return x + x;
        } else {
            return x;
        }
    }
    x = uint_as_float (w);
    return x;
}

int main (void)
{
    uint32_t argi, resi, refi;
    float arg, res, ref;

    argi = 0;
    do {
        arg = uint_as_float (argi);
        ref = newlib_roundf (arg);
        res = my_roundf (arg);
        resi = float_as_uint (res);
        refi = float_as_uint (ref);
        if (resi != refi) { // check for identical bit pattern
            printf ("!!!! arg=%08x  res=%08x  ref=%08x\n", argi, resi, refi);
            return EXIT_FAILURE;
        }
        argi++;
    } while (argi);
    return EXIT_SUCCESS;
}

मैंने int16 बिट्स से अधिक चौड़ा होने से बचने के लिए एक संपादन किया । यह अभी भी निश्चित है कि float4-बाइट IEEE754 बाइनरी 32 है। एक C ++ 11 static_assertया शायद एक मैक्रो #ifdef/ #errorयह जाँच सकता है। (लेकिन निश्चित रूप से यदि C ++ 11 उपलब्ध है, तो आपको उपयोग करना चाहिए std::round, या वर्तमान गोलाई मोड का उपयोग करना चाहिए std::rintजो जीसीसी और क्लैंग के साथ अच्छी तरह से इनलाइन करता है)।
पीटर कॉर्डेस

BTW, एक के लिए gcc -ffast-math -msse4.1inlines , और फिर एक । यानी के संदर्भ में यह काफी कुशलता से लागू होता है । लेकिन C ++ स्रोत में मैन्युअल रूप से ऐसा करने का कोई कारण नहीं है, क्योंकि यदि आपके पास या आपके पास भी है । एक गॉडबॉल्ट लिंक के लिए मेरा जवाब देखें और अलग-अलग जीसीसी / क्लैंग संस्करणों के साथ इनलाइन या नहीं का एक विस्तृत विवरण। std::round()add( AND(x, L1), OR(x,L2)roundsdroundrintstd::rint()std::nearbyint()std::round()
पीटर कॉर्डेस

@PeterCordes मैं अच्छी तरह से जानता हूं कि कैसे (जब उत्तरार्द्ध मोड दौर-से-निकटतम-या-यहां तक ​​कि ऑपरेटिंग चल रहा है) के round()संदर्भ में कुशलतापूर्वक लागू किया जाए rint(): मैंने CUDA मानक गणित पुस्तकालय के लिए लागू किया। हालाँकि, यह प्रश्न round()C ++ 11 से पहले C ++ के साथ लागू करने का तरीका पूछने के लिए लग रहा था , इसलिए rint()यह केवल floor()और केवल उपलब्ध नहीं होगा ceil()
njuffa

@PeterCordes क्षमा करें, मुझे याद आती है। गोल-से-शून्य मोड, उर्फ round()से आसानी से संश्लेषित किया rint()जाता है । पहली कॉफी से पहले जवाब नहीं दिया जाना चाहिए। trunc()
njuffa

1
@PeterCordes मैं सहमत हूं कि यह संभावना है कि ओपी को विशिष्ट दौर के व्यवहार की आवश्यकता नहीं है round(); अधिकांश प्रोग्रामर बस गोल-से-पास-पास के साथ round()बनाम के बीच के अंतर के बारे में नहीं जानते हैं rint(), जहां बाद को आमतौर पर सीधे हार्डवेयर द्वारा प्रदान किया जाता है और इसलिए अधिक कुशल; मैंने प्रोग्रामर्स को अवगत कराने के लिए CUDA प्रोग्रामिंग गाइड में कहा कि: "एकल-सटीक फ़्लोटिंग-पॉइंट संख्या होने के साथ एकल-सटीक फ़्लोटिंग-पॉइंट ऑपरेटर को गोल करने का अनुशंसित तरीका है rintf(), न कि roundf()"।
njuffa

0

मैं x86 आर्किटेक्चर और MS VS विशिष्ट C ++ के लिए asm में राउंड के निम्नलिखित कार्यान्वयन का उपयोग करता हूं:

__forceinline int Round(const double v)
{
    int r;
    __asm
    {
        FLD     v
        FISTP   r
        FWAIT
    };
    return r;
}

UPD: दोहरा मूल्य वापस करने के लिए

__forceinline double dround(const double v)
{
    double r;
    __asm
    {
        FLD     v
        FRNDINT
        FSTP    r
        FWAIT
    };
    return r;
}

आउटपुट:

dround(0.1): 0.000000000000000
dround(-0.1): -0.000000000000000
dround(0.9): 1.000000000000000
dround(-0.9): -1.000000000000000
dround(1.1): 1.000000000000000
dround(-1.1): -1.000000000000000
dround(0.49999999999999994): 0.000000000000000
dround(-0.49999999999999994): -0.000000000000000
dround(0.5): 0.000000000000000
dround(-0.5): -0.000000000000000

परिणाम मान डबल परिशुद्धता के साथ फ़्लोटिंग मान होना चाहिए।
सत्यसेकर

@ ट्रुसेकर: हाँ, मुझे आवश्यक प्रकार का रिटर्न वैल्यू देखना था। ठीक है, "UPD" देखें।
अलेक्सी एफ।

कंपाइलर उम्मीद से इनलाइन rint()या nearbyint()SSE4.1 roundsdइंस्ट्रक्शन या x87 frndintइंस्ट्रक्शन के लिए, जो कि एक रजिस्टर में डेटा पर इस इनलाइन asm का उपयोग करने के लिए आवश्यक दो स्टोर / रीलोड राउंड ट्रिप्स से बहुत तेज होगा। MSVC इनलाइन asm एक निर्देश को लपेटने के लिए काफी बेकार है frndintक्योंकि एक रजिस्टर में इनपुट प्राप्त करने का कोई तरीका नहीं है। परिणाम के साथ एक फ़ंक्शन के अंत में इसका उपयोग करना st(0)आउटपुट वापस करने के तरीके के रूप में विश्वसनीय हो सकता है; जाहिरा तौर पर यह eaxपूर्णांकों के लिए सुरक्षित है , यहां तक ​​कि जब यह फ़ंक्शन को समाहित करता है।
पीटर कॉर्डेस

@PeterCordes आधुनिक अनुकूलन का स्वागत है। हालाँकि मैं SSE4.1 का उपयोग करने में सक्षम नहीं था क्योंकि यह उस समय मौजूद नहीं था। मेरा उद्देश्य दौर के न्यूनतम कार्यान्वयन को प्रदान करना था जो 2000 के दशक से पुराने इंटेल पी 3 या पी 4 परिवारों पर भी कार्य कर सकता था।
एल्पसे एफ।

P3 में SSE2 भी नहीं है, इसलिए कंपाइलर पहले से ही x87 के लिए उपयोग कर रहा है double, और इस प्रकार इसके frndintलिए खुद को उत्सर्जित करने में सक्षम होना चाहिए rint()। यदि आपका कंपाइलर SSE2 का उपयोग कर रहा है, तो doubleएक्सएमएम रजिस्टर से x87 तक उछलकर वापस आना इसके लायक नहीं हो सकता है।
पीटर कॉर्डेस

0

"N" दशमलव स्थानों द्वारा फ्लोटिंग मान को पूर्ण करने का सबसे अच्छा तरीका, ओ (1) समय के साथ निम्नलिखित है: -

हमें 3 स्थानों अर्थात n = 3. से मान को राउंड ऑफ करना है,

float a=47.8732355;
printf("%.3f",a);

-4
// Convert the float to a string
// We might use stringstream, but it looks like it truncates the float to only
//5 decimal points (maybe that's what you want anyway =P)

float MyFloat = 5.11133333311111333;
float NewConvertedFloat = 0.0;
string FirstString = " ";
string SecondString = " ";
stringstream ss (stringstream::in | stringstream::out);
ss << MyFloat;
FirstString = ss.str();

// Take out how ever many decimal places you want
// (this is a string it includes the point)
SecondString = FirstString.substr(0,5);
//whatever precision decimal place you want

// Convert it back to a float
stringstream(SecondString) >> NewConvertedFloat;
cout << NewConvertedFloat;
system("pause");

यह रूपांतरण का एक अयोग्य गंदा तरीका हो सकता है लेकिन बिल्ली, यह काम करता है। और यह अच्छा है, क्योंकि यह वास्तविक फ्लोट पर लागू होता है। न केवल उत्पादन को नेत्रहीन रूप से प्रभावित करना।


यह प्रफुल्लित करने वाला अकुशल है, और यह निकटतम होने के बजाय ट्रंकट्स (हमेशा अनुगामी अंकों को त्यागकर) को काटता है।
पीटर कॉर्डेस

-6

इसे मैने किया है:

#include <cmath.h>

using namespace std;

double roundh(double number, int place){

    /* place = decimal point. Putting in 0 will make it round to whole
                              number. putting in 1 will round to the
                              tenths digit.
    */

    number *= 10^place;
    int istack = (int)floor(number);
    int out = number-istack;
    if (out < 0.5){
        floor(number);
        number /= 10^place;
        return number;
    }
    if (out > 0.4) {
        ceil(number);
        number /= 10^place;
        return number;
    }
}

3
क्या आपका मतलब बाइनरी ऑपरेटर ^ 10 ^ जगह में pow (10, प्लेस) से नहीं था? 10 ^ 2 मेरी मशीन पर मुझे 8 देता है !! फिर भी मेरे मैक 10.7.4 और जीसीसी पर, कोड काम नहीं करता है, मूल मूल्य लौटाता है।
Pete855217
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.