यदि मेरे पास निम्नलिखित घोषणा है:
float a = 3.0 ;
क्या वह त्रुटि है? मैं एक पुस्तक में पढ़ता हूं जो 3.0एक doubleमूल्य है और मुझे इसे निर्दिष्ट करना होगा float a = 3.0f। ऐसा है क्या?
यदि मेरे पास निम्नलिखित घोषणा है:
float a = 3.0 ;
क्या वह त्रुटि है? मैं एक पुस्तक में पढ़ता हूं जो 3.0एक doubleमूल्य है और मुझे इसे निर्दिष्ट करना होगा float a = 3.0f। ऐसा है क्या?
;बाद की जरूरत है ।
जवाबों:
यह घोषित करने में कोई त्रुटि नहीं है float a = 3.0: यदि आप करते हैं, तो संकलक डबल शाब्दिक 3.0 को आपके लिए एक फ्लोट में बदल देगा।
हालाँकि, आपको विशिष्ट परिदृश्यों में फ़्लोट शाब्दिक संकेतन का उपयोग करना चाहिए ।
प्रदर्शन कारणों के लिए:
विशेष रूप से, विचार करें:
float foo(float x) { return x * 0.42; }
यहां कंपाइलर प्रत्येक लौटे मूल्य के लिए एक रूपांतरण (जो आप रनटाइम पर भुगतान करेगा) का उत्सर्जन करेगा। इससे बचने के लिए आपको घोषणा करनी चाहिए:
float foo(float x) { return x * 0.42f; } // OK, no conversion required
परिणामों की तुलना करते समय बग से बचने के लिए:
उदाहरण के लिए निम्नलिखित तुलना विफल रहती है:
float x = 4.2;
if (x == 4.2)
std::cout << "oops"; // Not executed!
हम इसे फ्लोट शाब्दिक अंकन के साथ ठीक कर सकते हैं:
if (x == 4.2f)
std::cout << "ok !"; // Executed!
(नोट: निश्चित रूप से, यह नहीं है कि आपको सामान्य रूप से समानता के लिए फ्लोट या डबल संख्या की तुलना कैसे करनी चाहिए )
सही अधिभार फ़ंक्शन को कॉल करने के लिए (उसी कारण के लिए):
उदाहरण:
void foo(float f) { std::cout << "\nfloat"; }
void foo(double d) { std::cout << "\ndouble"; }
int main()
{
foo(42.0); // calls double overload
foo(42.0f); // calls float overload
return 0;
}
जैसा कि साइबर ने उल्लेख किया है , एक प्रकार की कटौती के संदर्भ में, कंपाइलर को घटाने में मदद करना आवश्यक है float:
के मामले में auto:
auto d = 3; // int
auto e = 3.0; // double
auto f = 3.0f; // float
और इसी तरह, टेम्पलेट प्रकार की कटौती के मामले में:
void foo(float f) { std::cout << "\nfloat"; }
void foo(double d) { std::cout << "\ndouble"; }
template<typename T>
void bar(T t)
{
foo(t);
}
int main()
{
bar(42.0); // Deduce double
bar(42.0f); // Deduce float
return 0;
}
42में एक पूर्णांक है, जो स्वचालित रूप से पदोन्नत हो जाता है float(और यह किसी भी सभ्य संकलक में संकलन समय पर होगा), इसलिए कोई प्रदर्शन जुर्माना नहीं है। शायद आपका मतलब कुछ ऐसा था 42.0।
4.2करने के लिए 4.2fस्थापित करने के पक्ष प्रभाव हो सकता है FE_INEXACT, झंडा संकलक और सिस्टम के आधार पर, और कुछ (वैसे कुछ) प्रोग्राम हैं जो ध्वज के लिए देखभाल के बारे में जो चल बिन्दु आपरेशनों सटीक हैं, और जो नहीं हैं, और परीक्षण करना । इसका मतलब यह है कि सरल स्पष्ट संकलन-समय परिवर्तन कार्यक्रम के व्यवहार को बदल देता है।
float foo(float x) { return x*42.0; }एकल-सटीक गुणा करने के लिए संकलित किया जा सकता है, और पिछली बार जब मैंने कोशिश की तो क्लैंग द्वारा संकलित किया गया था। हालांकि float foo(float x) { return x*0.1; }एक एकल-सटीक गुणन के लिए संकलित नहीं किया जा सकता है। हो सकता है कि इस पैच से पहले यह थोड़ा अधिक आशावादी हो, लेकिन पैच के बाद यह रूपांतरण-डबल_प्रेशर_ओप-रूपांतरण को एकल_प्रदर्शन_प में तब संयोजित करता है जब परिणाम हमेशा समान होता है। article.gmane.org/gmane.comp.compilers.llvm.cvs/167800/match=
someFloat, तो अभिव्यक्ति someFloat * 0.1की तुलना में अधिक सटीक परिणाम होंगे someFloat * 0.1f, जबकि कई मामलों में फ्लोटिंग-पॉइंट डिवीजन की तुलना में सस्ता है। उदाहरण के लिए, (फ्लोट) (167772208.0f * 0.1) 16777222 के बजाय 16777220 पर सही ढंग से राउंड करेगा। कुछ कंपाइलर doubleफ़्लोटिंग-पॉइंट डिवाइड के लिए एक गुणा कर सकते हैं, लेकिन उन लोगों के लिए जो यह बहुत सारे मानों के लिए सुरक्षित नहीं है। ) गुणा एक उपयोगी अनुकूलन हो सकता है, लेकिन केवल अगर एक doubleपारस्परिक के साथ प्रदर्शन किया ।
कंपाइलर निम्नलिखित में से किसी भी शाब्दिक को फ़्लोट में बदल देगा, क्योंकि आपने चर को फ्लोट के रूप में घोषित किया था।
float a = 3; // converted to float
float b = 3.0; // converted to float
float c = 3.0f; // float
यह बात है कि क्या आपने उपयोग किया है auto(या अन्य प्रकार की कटौती के तरीके), उदाहरण के लिए:
auto d = 3; // int
auto e = 3.0; // double
auto f = 3.0f; // float
autoयह एकमात्र मामला नहीं है।
प्रत्यय के बिना फ़्लोटिंग पॉइंट शाब्दिक प्रकार डबल होते हैं , यह मसौदा C ++ मानक अनुभाग 2.14.4 फ़्लोटिंग शाब्दिक :
[...] एक अस्थायी शाब्दिक का प्रकार दोहरा है जब तक कि एक प्रत्यय द्वारा स्पष्ट रूप से निर्दिष्ट नहीं किया जाता है। [...]
तो यह निर्दिष्ट करने के लिए एक त्रुटि है 3.0एक डबल शाब्दिक एक करने के लिए नाव ?:
float a = 3.0
नहीं, यह नहीं है, इसे रूपांतरित किया जाएगा, जो अनुभाग 4.8 फ़्लोटिंग बिंदु रूपांतरणों में शामिल है :
फ्लोटिंग पॉइंट प्रकार का एक प्रचलन दूसरे फ़्लोटिंग पॉइंट प्रकार के प्रचलन में परिवर्तित किया जा सकता है। यदि स्रोत मान को गंतव्य प्रकार में सटीक रूप से दर्शाया जा सकता है, तो रूपांतरण का परिणाम सटीक प्रतिनिधित्व है। यदि स्रोत मान दो आसन्न गंतव्य मानों के बीच है, तो रूपांतरण का परिणाम उन मूल्यों में से किसी एक का कार्यान्वयन-परिभाषित विकल्प है। अन्यथा, व्यवहार अपरिभाषित है।
हम GotW # 67 में इस के निहितार्थ पर अधिक विवरण पढ़ सकते हैं : डबल या कुछ भी नहीं जो कहता है:
इसका मतलब यह है कि एक डबल स्थिरांक को स्पष्ट रूप से (यानी, चुपचाप) एक फ्लोट स्थिरांक में परिवर्तित किया जा सकता है, भले ही ऐसा करने से सटीक (यानी, डेटा) खो जाए। यह सी संगतता और प्रयोज्य कारणों के लिए रहने की अनुमति दी गई थी, लेकिन यह ध्यान में रखने योग्य है जब आप फ्लोटिंग-पॉइंट काम करते हैं।
यदि आप अपरिभाषित व्यवहार करने की कोशिश करते हैं, तो एक गुणवत्ता संकलक आपको चेतावनी देगा, अर्थात् एक डबल मात्रा को एक फ्लोट में डालें जो न्यूनतम से कम है, या अधिकतम से अधिक है, एक फ्लोट का प्रतिनिधित्व करने में सक्षम है। वास्तव में अच्छा कंपाइलर एक वैकल्पिक चेतावनी प्रदान करेगा यदि आप ऐसा कुछ करने की कोशिश करते हैं जिसे परिभाषित किया जा सकता है, लेकिन जानकारी खो सकता है, अर्थात् एक फ्लोट में एक दोहरी मात्रा डाल सकता है जो फ्लोट द्वारा प्रतिनिधित्व करने योग्य न्यूनतम और अधिकतम मूल्यों के बीच है, लेकिन जो नहीं कर सकता एक नाव के रूप में प्रतिनिधित्व किया।
तो सामान्य मामले के लिए चेतावनी है कि आपको इसके बारे में पता होना चाहिए।
व्यावहारिक दृष्टिकोण से, इस मामले में परिणाम सबसे अधिक होने की संभावना होगी, भले ही तकनीकी रूप से कोई रूपांतरण हो, हम इसे गॉडबोल्ट पर निम्नलिखित कोड आज़माकर देख सकते हैं :
#include <iostream>
float func1()
{
return 3.0; // a double literal
}
float func2()
{
return 3.0f ; // a float literal
}
int main()
{
std::cout << func1() << ":" << func2() << std::endl ;
return 0;
}
और हम देखते हैं कि के लिए परिणाम func1और func2समान हैं, दोनों का उपयोग कर clangऔर gcc:
func1():
movss xmm0, DWORD PTR .LC0[rip]
ret
func2():
movss xmm0, DWORD PTR .LC0[rip]
ret
जैसा कि पास्कल इस टिप्पणी में बताते हैं कि आप हमेशा इस पर भरोसा नहीं कर पाएंगे। उपयोग करने 0.1और 0.1fक्रमशः उत्पन्न होने वाले असेंबली का कारण बनता है क्योंकि रूपांतरण अब स्पष्ट रूप से किया जाना चाहिए। निम्नलिखित कोड:
float func1(float x )
{
return x*0.1; // a double literal
}
float func2(float x)
{
return x*0.1f ; // a float literal
}
निम्नलिखित विधानसभा में परिणाम:
func1(float):
cvtss2sd %xmm0, %xmm0 # x, D.31147
mulsd .LC0(%rip), %xmm0 #, D.31147
cvtsd2ss %xmm0, %xmm0 # D.31147, D.31148
ret
func2(float):
mulss .LC2(%rip), %xmm0 #, D.31155
ret
भले ही आप यह निर्धारित कर सकें कि आपके इरादे को सही प्रकार के बेहतर दस्तावेजों का उपयोग करके रूपांतरण पर असर पड़ेगा या नहीं। उदाहरण के लिए एक स्पष्ट रूपांतरण का उपयोग static_castकरने से रूपांतरण को स्पष्ट करने में मदद मिलती है क्योंकि आकस्मिक रूप से विरोध किया गया था, जो बग या संभावित बग का संकेत दे सकता है।
ध्यान दें
जैसा कि सुपरकैट इंगित करता है, उदाहरण के लिए गुणा 0.1और 0.1fसमतुल्य नहीं है। मैं सिर्फ टिप्पणी उद्धृत करने जा रहा हूं क्योंकि यह उत्कृष्ट था और एक सारांश शायद यह न्याय नहीं करेगा:
उदाहरण के लिए, यदि f 100000224 के बराबर था (जो कि फ्लोट के रूप में प्रतिनिधित्व करने योग्य है), तो इसे दसवें से गुणा करने पर एक परिणाम प्राप्त करना चाहिए, जो 10000022 तक कम हो जाता है, लेकिन 0.1f से गुणा करने पर परिणाम निकलेगा, जो 10000023 तक गोल हो जाता है । यदि इरादा दस से विभाजित करना है, तो डबल निरंतर 0.1 से गुणा 10f द्वारा विभाजन की तुलना में तेजी से होगा, और 0.1f से गुणा से अधिक सटीक होगा।
मेरा मूल बिंदु एक अन्य प्रश्न में दिए गए झूठे उदाहरण को प्रदर्शित करना था लेकिन यह सूक्ष्म रूप से प्रदर्शित करता है कि खिलौने के उदाहरणों में सूक्ष्म मुद्दे मौजूद हो सकते हैं।
f = f * 0.1;और f = f * 0.1f; अलग-अलग चीजें करते हैं । उदाहरण के लिए, यदि f१०००००२२४ के बराबर था (जो कि वास्तव में एक के रूप में प्रतिनिधित्व करने योग्य है float), तो इसे दसवें से गुणा करने पर परिणाम निकलना चाहिए, जो १०००००२२ तक कम हो जाता है, लेकिन ०.१ एफ से गुणा करने पर परिणाम निकलेगा, जो १०००००२३ तक गलत है। इरादा दस से विभाजित करने के लिए है, doubleलगातार 0.1 से गुणा संभवतया विभाजन से तेज होगा 10f, और गुणा से अधिक सटीक होगा 0.1f।
यह इस अर्थ में कोई त्रुटि नहीं है कि कंपाइलर इसे अस्वीकार कर देगा, लेकिन यह इस अर्थ में एक त्रुटि है कि यह वह नहीं हो सकता जो आप चाहते हैं।
जैसा कि आपकी पुस्तक सही ढंग से बताती है, 3.0प्रकार का एक मूल्य है double। से एक अंतर्निहित रूपांतरण doubleहै float, इसलिए float a = 3.0;एक चर की वैध परिभाषा है।
हालांकि, कम से कम वैचारिक रूप से, यह एक अनावश्यक रूपांतरण करता है। संकलक के आधार पर, रूपांतरण संकलन समय पर किया जा सकता है, या इसे रन समय के लिए बचाया जा सकता है। रन टाइम के लिए इसे सहेजने का एक वैध कारण यह है कि फ्लोटिंग-पॉइंट रूपांतरण मुश्किल हैं और अप्रत्याशित साइड इफेक्ट्स हो सकते हैं यदि मूल्य का सही प्रतिनिधित्व नहीं किया जा सकता है, और यह सत्यापित करना हमेशा आसान नहीं है कि क्या मूल्य का बिल्कुल प्रतिनिधित्व किया जा सकता है।
3.0f उस समस्या से बचा जाता है: हालांकि तकनीकी रूप से, कंपाइलर को अभी भी रन टाइम पर स्थिर गणना करने की अनुमति है (यह हमेशा है), यहां, कोई कारण नहीं है कि कोई भी कंपाइलर संभवतः ऐसा कर सकता है।
जबकि त्रुटि नहीं, प्रति se, यह थोड़ा मैला है। आप जानते हैं कि आप एक फ्लोट चाहते हैं, इसलिए इसे एक फ्लोट के साथ आरंभ करें।
एक और प्रोग्रामर साथ आ सकता है और यह सुनिश्चित नहीं किया जाना चाहिए कि घोषणा का कौन सा हिस्सा सही है, टाइप या इनिशलाइज़र। क्यों न दोनों सही हों?
float Answer = 42.0f;
जब आप किसी वैरिएबल को परिभाषित करते हैं, तो इसे इनिशियलाइज़र के साथ इनिशियलाइज़ किया जाता है। इसके लिए इनिशियलाइज़र के मान को उस वैरिएबल के प्रकार में परिवर्तित करने की आवश्यकता हो सकती है जिसे इनिशियलाइज़ किया जा रहा है। ऐसा तब होता है जब आप कहते हैं float a = 3.0;: इनिशलाइज़र का मान परिवर्तित हो जाता है float, और रूपांतरण का परिणाम प्रारंभिक मान बन जाता है a।
यह आम तौर पर ठीक है, लेकिन यह लिखने के 3.0fलिए चोट नहीं करता है कि आप जानते हैं कि आप क्या कर रहे हैं, और खासकर अगर आप लिखना चाहते हैं auto a = 3.0f।
यदि आप निम्नलिखित प्रयास करते हैं:
std::cout << sizeof(3.2f) <<":" << sizeof(3.2) << std::endl;
आपको आउटपुट मिलेगा:
4:8
पता चलता है कि, 3.2f का आकार 32-बिट मशीन व्हेरेस पर 4 बाइट्स के रूप में लिया जाता है 3.2 को 32-बिट मशीन पर 8 बाइट्स लेने वाले दोहरे मूल्य के रूप में व्याख्या की जाती है। यह वह उत्तर प्रदान करना चाहिए जिसकी आपको तलाश है।
doubleऔर floatइसका जवाब नहीं है कि क्या आप floatएक डबल शाब्दिक से शुरू कर सकते हैं
कंपाइलर शाब्दिक रूप से सर्वश्रेष्ठ-फिटिंग प्रकार को कम करता है, या लीज़ पर जो यह सोचता है कि वह सबसे उपयुक्त है। यह सटीक पर दक्षता खोना है, यानी फ्लोट के बजाय एक डबल का उपयोग करें। यदि संदेह है, तो इसे स्पष्ट करने के लिए ब्रेस-इंट्राइज़र का उपयोग करें:
auto d = double{3}; // make a double
auto f = float{3}; // make a float
auto i = int{3}; // make a int
कहानी तब और दिलचस्प हो जाती है जब आप दूसरे चर से टाइप करते हैं जहां टाइप-कन्वर्शन नियम लागू होते हैं: जबकि यह एक शाब्दिक रूप से दोहरे रूप को कम करने के लिए कानूनी है, लेकिन यह संभव संकरेपन के बिना इंट से दूषित हो सकता है:
auto xxx = double{i} // warning ! narrowing conversion of 'i' from 'int' to 'double'
3.0को आपके लिए एक फ्लोट में बदल देगा । अंतिम परिणाम से अप्रभेद्य हैfloat a = 3.0f।