এই ভাসমান পয়েন্ট অপ্টিমাইজেশন অনুমোদিত?


90

আমি floatবড় সংখ্যার সংখ্যার সঠিকভাবে প্রতিনিধিত্ব করার ক্ষমতাটি কোথায় হারিয়েছে তা দেখার চেষ্টা করেছি । সুতরাং আমি এই ছোট স্নিপেট লিখেছি:

int main() {
    for (int i=0; ; i++) {
        if ((float)i!=i) {
            return i;
        }
    }
}

এই কোডটি ঝাঁকুনি বাদে সমস্ত সংকলকগুলির সাথে কাজ করে বলে মনে হচ্ছে। ঝনঝন একটি সাধারণ অসীম লুপ উত্পন্ন করে। Godbolt

এটি কি অনুমোদিত? যদি হ্যাঁ, এটি কিউইআই ইস্যু?


@ গেজা আমি ফলাফলের সংখ্যাটি শুনতে আগ্রহী!
নদা

4
gccআপনি যদি এর -Ofastপরিবর্তে সংকলন করেন তবে একই অসীম লুপ অপটিমাইজেশন করে, সুতরাং এটি একটি অপ্টিমাইজেশন gccঅনিরাপদ বলে মনে করে, তবে এটি এটি করতে পারে।
12345ie

4
g ++ একটি অসীম লুপও জেনারেট করে, তবে এটি এর ভিতর থেকে কাজটি অপ্টিমাইজ করে না। এটি নিজের সাথে ucomiss xmm0,xmm0তুলনা করতে দেখতে পাবে (float)i। এটিই ছিল আপনার প্রথম ক্লু যা আপনার সি ++ উত্সের অর্থ এই নয় যে আপনি কী ভেবেছিলেন। আপনি কি দাবি করছেন যে আপনি এই লুপটি মুদ্রণ / ফেরতের জন্য পেয়েছেন 16777216? কি সংকলক / সংস্করণ / বিকল্প ছিল? কারণ এটি একটি সংকলক বাগ হবে। জিসিসি আপনার কোডটিকেjnp লুপ শাখা হিসাবে সঠিকভাবে অনুকূল করেছে ( Godbolt.org/z/XJYWeu ): যতক্ষণ অপারেশনগুলি নাএন!= ছিল না ততক্ষণ লুপিং চালিয়ে যান ।
পিটার কর্ডেস

4
বিশেষত, এটি সেই -ffast-mathবিকল্প যা স্পষ্টতই সক্ষম করে এতে -Ofastজিসিসি অনিরাপদ ভাসমান-পয়েন্ট অপ্টিমাইজেশানগুলি প্রয়োগ করতে দেয় এবং এইভাবে ক্ল্যাংয়ের মতো একই কোড তৈরি করতে পারে। এমএসভিসি ঠিক একইভাবে আচরণ করে: ছাড়া /fp:fast, এটি কোডের একটি গুচ্ছ তৈরি করে যার ফলস্বরূপ অসীম লুপ; সহ /fp:fast, এটি একটি একক jmpনির্দেশ প্রকাশ করে। আমি ধরে নিচ্ছি যে স্পষ্টভাবে অনিরাপদ এফপি অপ্টিমাইজেশানগুলি চালু না করে এই সংকলকগুলি NaN মানগুলি সম্পর্কিত আইইইই 754 প্রয়োজনীয়তার জন্য স্থগিত হয়ে যায়। বরং আকর্ষণীয় যে ঝাঁকুনি না, আসলে। এর স্ট্যাটিক বিশ্লেষক আরও ভাল। @ 12345ieee
কোডি গ্রে

4
@ গেজা: কোডটি যদি আপনি যা করতে চান তা করেন, যখন গাণিতিক মানটির গাণিতিক মান থেকে (float) iআলাদা হয় তা পরীক্ষা করা হয় i, তবে ফলাফল ( returnবিবৃতিতে প্রত্যাবর্তিত মান ) 16,777,217 হবে, 16,777,216 নয়।
এরিক পোস্টপিসিল

উত্তর:


49

@ অঞ্জিউ যেমন উল্লেখ করেছেন , !=অপারেটরের উভয় পক্ষের একই ধরণের প্রয়োজন। (float)i != iআরএইচএসের প্রচারের ফলাফলটিও ভাসতে শুরু করে, তাই আমাদের কাছে রয়েছে (float)i != (float)i


g ++ একটি অসীম লুপও জেনারেট করে, তবে এটি এর ভিতর থেকে কাজটি অপ্টিমাইজ করে না। আপনি এটি রূপান্তর করতে পারবেন-> এর সাথে ভাসা cvtsi2ssএবং নিজের সাথে ucomiss xmm0,xmm0তুলনা (float)iকরতে পারেন। (এটিই ছিল আপনার প্রথম সূত্র যা আপনার সি ++ উত্সের অর্থ এটি নয় যা আপনি এটি ভেবেছিলেন এটি পছন্দ করেছেন @ অ্যাঞ্জের উত্তর ব্যাখ্যা করে))

x != xএটি তখনই সত্য যখন এটি "অর্ডারড" হয় কারণ xএটি এনএন ছিল। ( INFINITYআইইইই গণিতে নিজের সাথে সমান তুলনা করে তবে এনএনএন NAN == NANমিথ্যা নয়, NAN != NANসত্য)।

gcc7.4 এবং তার চেয়ে বেশি পুরানো আপনার কোডটিকে jnpলুপ শাখা হিসাবে যথাযথভাবে অনুকূল করে তোলে ( https://godbolt.org/z/fyOhW1 ): যতক্ষণ অপারেশনগুলি x != x NaN ছিল না ততক্ষণ লুপিং চালিয়ে যান । (gcc8 এবং পরবর্তীকালে jeলুপটি বিরতিতে পরীক্ষা করে, এটি কোনও নন-এনএন ইনপুট জন্য সর্বদা সত্য হবে এই তথ্যের উপর ভিত্তি করে অনুকূলিতকরণে ব্যর্থ হয়েছে)। x86 এফপি অর্ডারযুক্ত উপর সেট পিএফ তুলনা করে।


আর BTW, মানে ঝনঝন এর অপ্টিমাইজেশান এছাড়াও নিরাপদ : এটা শুধু সিএসই হয়েছে (float)i != (implicit conversion to float)iএকই হচ্ছে, এবং প্রমাণ করা যে i -> floatসম্ভাব্য পরিসর জন্য nan না হয় int

(যদিও এই লুপটি স্বাক্ষরিত-ওভারফ্লো ইউবিতে আঘাত করবে, এটি ud2অবৈধ নির্দেশনা সহ , বা লুপের বডিটি আসলে কী ছিল তা নির্বিশেষে একটি ফাঁকা অসীম লুপটি আক্ষরিক অর্থে যে কোনও এসেম ছাড়তে পারে allowed ) তবে স্বাক্ষরিত ওভারফ্লো ইউবি উপেক্ষা করে , এই অপ্টিমাইজেশানটি এখনও 100% আইনী।


স্বাক্ষরিত-পূর্ণসংখ্যা ওভারফ্লো ভালভাবে সংজ্ঞায়িত করা (2 এর পরিপূরক wraparound হিসাবে) এমনকি-fwrapv লুপ বডিটি অপ্টিমাইজ করতে ব্যর্থ হয় G https://godbolt.org/z/t9A8t_

এমনকি সক্ষম করাও -fno-trapping-mathসহায়তা করে না। (জিসিসির ডিফল্ট দুর্ভাগ্যক্রমে সক্ষম করা
-ftrapping-mathসত্ত্বেও এটি জিসিসির প্রয়োগটি ভাঙ্গা / বগি হওয়া সত্ত্বেও সক্ষম হয়েছে )) ইনট-> ফ্লোট রূপান্তর কোনও এফপির নিখরচায় ব্যতিক্রম ঘটতে পারে (সংখ্যায় খুব বড় সংখ্যার পক্ষে হুবহু উপস্থাপন করা যায়), তাই ব্যতিক্রমগুলি সম্ভবত অনমাস্ক করা যুক্তিসঙ্গত নয় লুপ শরীর দূরে অপ্টিমাইজ করুন। (কারণ ভাসায় রূপান্তর 16777217করা যদি পর্যবেক্ষণযোগ্য পার্শ্ব-প্রতিক্রিয়া ঘটাতে পারে তবে যদি অক্ষত ব্যতিক্রমটি আনমস্ক করা না থাকে))

তবে এর সাথে -O3 -fwrapv -fno-trapping-mathএটি খালি অসীম লুপকে সংকলন না করার জন্য এটি 100% মিসড অপটিমাইজেশন। ছাড়া #pragma STDC FENV_ACCESS ON, মুখোশযুক্ত এফপি ব্যতিক্রমগুলি রেকর্ড করে এমন স্টিকি ফ্ল্যাগগুলির অবস্থা কোডটির একটি পর্যবেক্ষণযোগ্য পার্শ্ব-প্রতিক্রিয়া নয়। না int-> floatরূপান্তর NaN এর ফলস্বরূপ x != xহতে পারে , তাই সত্য হতে পারে না।


এই সংকলকগুলি সমস্ত সি ++ বাস্তবায়নের জন্য অপ্টিমাইজ করছে যা আইইইই 754 একক-নির্ভুলতা (বাইনারি 32) floatএবং 32-বিট ব্যবহার করে int

Bugfixed(int)(float)i != i লুপ সংকীর্ণ 16 বিট সঙ্গে সি ++ বাস্তবায়নের উপর UB হবে intএবং / অথবা ব্যাপকতর floatকারণ আপনার আঘাত করি, সাইন-পূর্ণসংখ্যা ওভারফ্লো UB প্রথম পূর্ণসংখ্যা করে একটি যেমন ঠিক representable ছিল না উপনীত হওয়ার আগে float

X86-64 সিস্টেম ভি এবিআই-র সাথে জিসিসি বা ঝাঁকুনির মতো প্রয়োগের জন্য সংকলন করার সময়, ইউবি-র প্রয়োগের জন্য নির্ধারিত বিভিন্ন সংকলনের অধীনে কোনও নেতিবাচক পরিণতি হয় না।


বিটিডাব্লু, আপনি সংজ্ঞায়িত থেকে FLT_RADIXএবং এই লুপটির ফলাফলটি স্থিরভাবে গণনা করতে পারেন । বা কমপক্ষে আপনি তাত্ত্বিকভাবে বলতে পারেন, যদি সত্যই কোনও আইজিইই ফ্লোটের মডেলটি ফিট করে তবে কোনও পজিট / আনুমের মতো অন্য কোনও ধরণের আসল-সংখ্যা উপস্থাপনার চেয়ে।FLT_MANT_DIG<climits>float

আমি নিশ্চিত নই যে floatআচরণের বিষয়ে আইএসও সি ++ স্ট্যান্ডার্ড নখগুলি কতটা নিচে রেখেছিল এবং কোনও ফর্ম্যাট যা স্থির-প্রস্থের এক্সপোনিস্ট এবং তাত্পর্যপূর্ণ ক্ষেত্রগুলির উপর ভিত্তি করে না ছিল মানদণ্ড অনুসারে হবে কিনা।


মন্তব্যে:

@ গেজা আমি ফলাফলের সংখ্যাটি শুনতে আগ্রহী!

@ নাদা: এটি 16777216

আপনি কি দাবি করছেন যে আপনি এই লুপটি মুদ্রণ / ফেরতের জন্য পেয়েছেন 16777216?

আপডেট: যেহেতু সেই মন্তব্য মুছে ফেলা হয়েছে, আমার মনে হয় না। সম্ভবত ওপি floatপ্রথম পূর্ণসংখ্যার ঠিক আগে উদ্ধৃতি দিচ্ছে যা 32-বিট হিসাবে ঠিক উপস্থাপন করা যায় না floathttps://en.wikedia.org/wiki/Single-precision_floating- point_format# Precision_limits_on_integer_values অর্থাত্ তারা কীভাবে এই বগি কোডটি দিয়ে যাচাই করার প্রত্যাশা করেছিল।

Bugfixed সংস্করণ would অবশ্যই মুদ্রণ 16777217, প্রথম পূর্ণসংখ্যা যে না ঠিক representable, বরং তার আগে মানের চেয়ে।

(সমস্ত উচ্চতর ভাসমান মানগুলি হুবহু পূর্ণসংখ্যা হয় তবে তা তাত্ত্বিক প্রস্থের চেয়ে বেশি পরিমাণের মানগুলির জন্য 2, তারপরে 4, তারপরে 8 ইত্যাদির গুণক হয় Many অনেকগুলি উচ্চতর পূর্ণসংখ্যার মান উপস্থাপন করা যায় তবে শেষ স্থানে 1 ইউনিট (তাত্পর্যপূর্ণ) 1 এর চেয়ে বড় হয় সুতরাং তারা সংখ্যার পূর্ণসংখ্যার হয় না The বৃহত্তম সীমাবদ্ধতা কেবল float2 ^ 128 এর নিচে থাকে, এটি এমনকি সমান জন্য খুব বড় int64_t)

যদি কোনও সংকলক আসল লুপ থেকে প্রস্থান করে এবং মুদ্রণ করে তবে এটি একটি সংকলক বাগ হবে।


4
@ সম্বেরোচিকেন: না, আমি প্রথমে ইলেক্ট্রনিক্স শিখলাম (কিছু পাঠ্যপুস্তক থেকে আমার বাবা পড়েছিলেন; তিনি একজন পদার্থবিজ্ঞানের অধ্যাপক ছিলেন), তারপরে ডিজিটাল যুক্তি দিয়ে সিপিইউ / সফটওয়্যারটিতে প্রবেশ করি। : পি এত সুন্দর আমি গ্রাউন্ড আপ থেকে জিনিসগুলি বরাবরই বুঝতে পছন্দ করেছি, বা যদি আমি উচ্চতর স্তর দিয়ে শুরু করি তবে আমি নীচের স্তরের সম্পর্কে কমপক্ষে কিছু শিখতে চাই যা নীচে স্তরের কীভাবে / কেন কাজ করে তা প্রভাবিত করে I'm চিন্তা করছি. (যেমন asm কীভাবে কাজ করে এবং কীভাবে এটি অপ্টিমাইজ করা যায় তা সিপিইউ ডিজাইনের সীমাবদ্ধতা / সিপিইউ-আর্কিটেকচার স্টাফ দ্বারা প্রভাবিত হয় turn যা পরিবর্তে পদার্থবিদ্যা + গণিত থেকে আসে from)
পিটার

4
জিসিসি হয়ত এর সাথেও অনুকূলিত করতে সক্ষম না হতে পারে frapwতবে আমি নিশ্চিত যে জিসিসি 10 এর -ffinite-loopsমতো পরিস্থিতিতে তৈরি করা হয়েছিল।
এমসিসিএস

64

মনে রাখবেন যে বিল্ট-ইন অপারেটরটির !=অপারেশনগুলি একই ধরণের হওয়া দরকার এবং প্রয়োজনে প্রচার এবং রূপান্তরগুলি ব্যবহার করে তা অর্জন করবে। অন্য কথায়, আপনার অবস্থা সমান:

(float)i != (float)i

এটি কখনই ব্যর্থ হওয়া উচিত নয় এবং তাই কোডটি শেষ পর্যন্ত iআপনার প্রোগ্রামটিকে অনির্ধারিত আচরণ দেয় over যে কোনও আচরণ তাই সম্ভব।

আপনি যা যাচাই করতে চান তা সঠিকভাবে পরীক্ষা করতে, আপনাকে ফলাফলটি এখানে ফিরে যেতে হবে int:

if ((int)(float)i != i)

8
@ ডুরিস এটি ইউবি। সেখানে হয় কেউ নির্দিষ্ট ফলাফল। সংকলক বুঝতে পারে যে এটি কেবলমাত্র ইউবিতে শেষ হতে পারে এবং লুপটি সম্পূর্ণরূপে সরানোর সিদ্ধান্ত নিতে পারে।
ফান্ড মনিকার লসুইট

4
@ ওপা মানে static_cast<int>(static_cast<float>(i))? reinterpret_castসেখানে স্পষ্টতই ইউবি রয়েছে
কালেথ

6
@ নিক হার্টলি: আপনি কি বলছেন (int)(float)i != iইউবি? আপনি কীভাবে এই সিদ্ধান্তে পৌঁছবেন? হ্যাঁ এটি বাস্তবায়িত সংজ্ঞায়িত বৈশিষ্ট্যের উপর নির্ভর করে (কারণ floatআইইইই 75 b৫ বাইনারি 32 হওয়ার প্রয়োজন নেই), তবে যে কোনও প্রয়োগের ক্ষেত্রে এটি সঠিকভাবে সংজ্ঞায়িত হয় যতক্ষণ floatনা হুবহু সমস্ত ধনাত্মক intমানগুলিকে উপস্থাপন করতে পারে যাতে আমরা স্বাক্ষরকৃত পূর্ণসংখ্যার ওভারফ্লো ইউবি পাই। ( en.cppreferences.com/w/cpp/tyype/climits এটি নির্ধারণ করে FLT_RADIXএবং এটি FLT_MANT_DIGনির্ধারণ করে)। সাধারণ মুদ্রণ বাস্তবায়ন-সংজ্ঞায়িত জিনিসগুলিতে, যেমন std::cout << sizeof(int)ইউবি নয় ...
পিটার কর্ডেস

4
@ ক্যালাথ: reinterpret_cast<int>(float)একেবারে ইউবি নয়, এটি কেবল একটি বাক্য গঠন / ত্রুটিযুক্ত। এই সিনট্যাক্সটি ফ্লোটের টাইপ-পাঞ্চিংয়ের intবিকল্প হিসাবে memcpy(যা ভালভাবে সংজ্ঞায়িত করা হয়েছে) বিকল্প হিসাবে অনুমতি দিলে ভাল হবে তবে reinterpret_cast<>আমি মনে করি কেবল পয়েন্টার টাইপগুলিতেই কাজ করে, আমি মনে করি।
পিটার কর্ডেস

4
@ পিটার জাস্ট ফর এনএএন, x != xসত্য। কলিরুতে লাইভ দেখুন । সি তেও।
Deduplicator
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.