উইন 32 এ স্বাক্ষরবিহীন আইটেমে ডাবল কাস্টটি 2,147,483,648 এ কেটে যাচ্ছে


85

নিম্নলিখিত কোড সংকলন:

ফলাফল (এমএসভিসি x86):

INT_MAX: 2147483647
UINT_MAX: 4294967295
Double value: 2147483649.000000
Direct cast value: 2147483648
Indirect cast value: 2147483649

আউটপুট (এমএসভিসি x64):

INT_MAX: 2147483647
UINT_MAX: 4294967295
Double value: 2147483649.000000
Direct cast value: 2147483649
Indirect cast value: 2147483649

ইন মাইক্রোসফট ডকুমেন্টেশন থেকে ধর্মান্তর সর্বোচ্চ মান পূর্ণসংখ্যা স্বাক্ষর করেছেন কোনো উল্লেখ নেই doubleযাও unsigned int

উপরের সমস্ত মানগুলি যখন কোনও ফাংশনটির প্রত্যাবর্তন INT_MAXহয় 2147483648তখন তা কেটে নেওয়া হয় ।

আমি প্রোগ্রামটি তৈরি করতে ভিজ্যুয়াল স্টুডিও 2019 ব্যবহার করছি । এটি জিসিসিতে ঘটে না ।

আমি কিছু ভুল করছি? রূপান্তর doubleকরার কোনও নিরাপদ উপায় আছে কি unsigned int?


24
এবং না, আপনি কোনও ভুল করছেন না (সম্ভবত মাইক্রোসফ্টের "সি" সংকলকটি ব্যবহার করার চেষ্টা করা ছাড়াও)
আন্তি হাপালা

4
আমার মেশিনে কাজ করে V, VS2017 v15.9.18 এবং VS2019 v16.4.1 এ পরীক্ষিত। সহায়তা ব্যবহার করুন> প্রতিক্রিয়া পাঠান> আপনার সংস্করণ সম্পর্কে তাদের জানাতে একটি বাগের প্রতিবেদন করুন।
হান্স প্যাস্যান্ট

4
আমি পুনরুত্পাদন করতে সক্ষম, আমার ওপি'র মতো ফলাফল রয়েছে। ভিএস2019 16.7.3।
anastaciu

4
@ এরিকপোস্টপিসিল সত্যিই এটির বিট-প্যাটার্নINT_MIN
এন্টি হাপালা

উত্তর:


70

একটি সংকলক বাগ ...

@ অ্যানাস্টাসিউ দ্বারা সরবরাহিত সমাবেশ থেকে, সরাসরি কাস্ট কোড কোড কল করে __ftol2_sse, যা মনে হয় এই সংখ্যাটিকে একটি স্বাক্ষরিত দীর্ঘতে রূপান্তরিত করে । রুটিনের নাম হ'ল ftol2_sseএটি একটি সিএস-সক্ষম সক্ষম মেশিন - তবে ভাসাটি একটি x87 ফ্লোটিং পয়েন্ট রেজিস্টারে রয়েছে।

; Line 17
    call    _getDouble
    call    __ftol2_sse
    push    eax
    push    OFFSET ??_C@_0BH@GDLBDFEH@Direct?5cast?5value?3?5?$CFu?6@
    call    _printf
    add esp, 8

অন্যদিকে পরোক্ষ castালাই করেন

; Line 18
    call    _getDouble
    fstp    QWORD PTR _d$[ebp]
; Line 19
    movsd   xmm0, QWORD PTR _d$[ebp]
    call    __dtoui3
    push    eax
    push    OFFSET ??_C@_0BJ@HCKMOBHF@Indirect?5cast?5value?3?5?$CFu?6@
    call    _printf
    add esp, 8

যা স্থানীয় ভেরিয়েবলের দ্বিগুণ মানকে পপ করে এবং সংরক্ষণ করে, তারপরে এটিকে এসএসই রেজিস্ট্রারে লোড করে এবং কল করে __dtoui3যা স্বাক্ষরবিহীন অন্তর্দৃষ্টি রূপান্তরের রুটিন ...

সরাসরি কাস্টের আচরণটি সি 98 এর সাথে খাপ খায় না; বা এটি পরবর্তী কোনও সংশোধনের সাথে সামঞ্জস্য করে না - এমনকি সি 89 এমনকি স্পষ্টভাবে বলেছে যে:

অবিচ্ছেদ্য প্রকারের মান স্বাক্ষরিত প্রকারে রূপান্তরিত হওয়ার সময় অবশিষ্ট অপারেশন সম্পন্ন করা হয় যখন ভাসমান প্রকারের মানটি স্বাক্ষরযুক্ত প্রকারে রূপান্তরিত হয়। সুতরাং পোর্টেবল মানগুলির পরিসীমা [0, Utype_MAX + 1)


আমি বিশ্বাস করি যে সমস্যাটি ২০০৫ সাল থেকে__ftol2 এটির ধারাবাহিকতা হতে পারে - এখানে একটি রূপান্তর ফাংশন ব্যবহৃত হত যা সম্ভবত এই কোডটির জন্য কাজ করত, অর্থাৎ এটি মানটিকে একটি স্বাক্ষরিত সংখ্যায় -2147483647 এ রূপান্তরিত করত, যা সঠিক উত্পাদন করত একটি স্বাক্ষরবিহীন সংখ্যা ব্যাখ্যা করা হলে ফলাফল।

দুর্ভাগ্যক্রমে এর __ftol2_sseজন্য একটি ড্রপ-ইন প্রতিস্থাপন নয় __ftol2, যেমন এটি হ'ল - কেবলমাত্র সর্বনিম্ন-তাত্পর্যপূর্ণ মান বিটগুলি যেমন রয়েছে ঠিক তেমন বদলে LONG_MIN/ ফেরত দিয়ে সীমার বাইরে ত্রুটি সংকেত দেয় 0x80000000, যা এখানে স্বাক্ষরবিহীন দীর্ঘ হিসাবে ব্যাখ্যা করা হয় না সব কি আশা করা হয়েছিল। আচরণকে __ftol2_sseজন্য বৈধ হবে signed long, একটি ডবল একটি মান রূপান্তর যেমন> LONG_MAXথেকে signed longঅনির্ধারিত আচরণ হবে।


23

অ্যান্টিহাপালার উত্তর অনুসরণ করে , আমি অপটিমাইজেশন ব্যবহার করে কোডটি পরীক্ষা করেছি /Oxএবং দেখেছি যে এটি __ftol2_sseআর ব্যবহার করা হয়নি বলে বাগটি সরিয়ে ফেলবে :

অপ্টিমাইজেশানগুলি ইনলাইন করা হয়েছে getdouble()এবং ধ্রুবক অভিব্যক্তি মূল্যায়ন যুক্ত করেছে যাতে রানটাইম সময়ে কোনও রূপান্তর প্রয়োজন বগকে দূরে সরিয়ে দেয়।

কৌতূহলের মাত্র বাইরে, আমি আরও কিছু পরীক্ষা করেছিলাম, নামটি রানটাইমে ফ্লোট-টু-ইন রূপান্তরকে বাধ্য করার জন্য কোড পরিবর্তন করে। এই ক্ষেত্রে ফলাফলটি এখনও সঠিক, __dtoui3উভয় রূপান্তরগুলিতে অপ্টিমাইজেশান সহ সংকলক ব্যবহার করে:

তবে ইনলাইনিং প্রতিরোধ __declspec(noinline) double getDouble(){...}করে বাগটি ফিরিয়ে আনবে:

__ftol2_sse2147483648উভয় পরিস্থিতিতে আউটপুট তৈরি করে উভয় রূপান্তরকে ডাকা হয়, @ জেওল সন্দেহগুলি সঠিক ছিল।


সংকলনের বিশদ:

  • কমান্ড লাইন ব্যবহার করে:
cl /permissive- /GS /analyze- /W3 /Gm- /Ox /sdl /D "WIN32" program.c        
  • ভিজ্যুয়াল স্টুডিওতে:

    • অক্ষম করা হচ্ছে RTCমধ্যে Project -> Properties -> Code Generationএবং সেটিং বেসিক রানটাইম চেক করতে ডিফল্ট

    • মধ্যে অপ্টিমাইজেশান সক্ষম করা হলে তা Project -> Properties -> Optimizationএবং সেটিং অপ্টিমাইজেশান থেকে / অক্স

    • x86মোডে ডিবাগার সহ ।


4
তারা কীভাবে "অপ্টিমাইজেশান সক্ষম করার সাথে ঠিক আছে, অপরিবর্তিত আচরণটি সত্যই অপরিজ্ঞাত হবে" => কোডটি আসলে সঠিকভাবে কাজ করে: ফ
এন্টি হাপালা

4
আন্টিহাপালা, হ্যাঁ, হ্যাঁ, মাইক্রোসফ্ট এর সর্বোত্তমতম।
anastaciu

4
প্রয়োগ করা অপ্টিমাইজেশনগুলি ইনলাইনিং এবং তারপরে ধ্রুবক প্রকাশের মূল্যায়ন ছিল ation এটি আর রানটাইমে ফ্ল্যাট-টু-ইন রূপান্তর করছে না। আমি ভাবছি যে আপনি যদি getDoubleলাইনের বাইরে চলে যেতে বাধ্য হন এবং / অথবা এটির এমন কোনও মান ফেরত দিতে পরিবর্তন করে যা সংকলক প্রমাণ করতে পারে না ধ্রুবক।
zwol

4
@ জওয়ল, আপনি ঠিক বলেছেন, লাইনটি বাইরে চাপিয়ে দেওয়া এবং ধ্রুবক মূল্যায়ন প্রতিরোধ করা বাগটি ফিরিয়ে আনতে পারে, তবে এবার উভয় রূপান্তরেই।
anastaciu

7

এমএস এর জন্য কেউ এএসমের দিকে তাকাতে পারেনি __ftol2_sse

ফলাফল থেকে, আমরা অনুমান করতে পারি যে এটি সম্ভবত x87 থেকে স্বাক্ষরিত int/ long(উইন্ডোজে 32-বিট উভয় প্রকারের) রূপান্তরিত হয়েছে , নিরাপদে পরিবর্তে uint32_t

x86 এফপি -> পূর্ণসংখ্যার নির্দেশাবলী যা পূর্ণসংখ্যার ফলাফলকে কেবল আবৃত / কাটবে না: গন্তব্যটিতে সঠিক মানটি উপস্থাপনযোগ্য না হলে তারা ইন্টেলকে "পূর্ণসংখ্যা অনির্দিষ্ট" বলে ডাকে : উচ্চ বিট সেট, অন্যান্য বিট পরিষ্কার। অর্থাত0x80000000

(অথবা যদি এফপি অবৈধ ব্যতিক্রমটি মুখোশ না থাকে তবে তা আগুন ধরিয়ে দেয় এবং কোনও মান সংরক্ষণ করা হয় না But তবে ডিফল্ট এফপি পরিবেশে সমস্ত এফপি ব্যতিক্রমগুলি মুখোশযুক্ত That's তাই এফপি গণনার জন্য আপনি একটি ত্রুটির পরিবর্তে একটি এনএএন পেতে পারেন))

এর মধ্যে x87 নির্দেশাবলী যেমন fistp(বর্তমান রাউন্ডিং মোড ব্যবহার করে) এবং এসএসই 2 নির্দেশাবলী অন্তর্ভুক্ত রয়েছে cvttsd2si eax, xmm0(0 এর দিকে কাটা ব্যবহার করা, এটি অতিরিক্ত tঅর্থ কি )।

সুতরাং এটি একটি কল double- - unsignedরূপান্তরকে সংকলন করতে একটি বাগ __ftol2_sse


পার্শ্ব-নোট / স্পর্শকাতর:

X86-64 এ, এফপি -> uint32_t সংকলন করা যেতে পারে cvttsd2si rax, xmm0, একটি 64-বিট স্বাক্ষরিত গন্তব্যে রূপান্তর করে, ইন্টিজার গন্তব্যের নিম্ন অর্ধেক (EAX) আপনি চান uint32_t উত্পাদন করে producing

এটি সি এবং সি ++ ইউবি ফলাফল যদি 0..2 ^ 32-1 সীমার বাইরে থাকে তবে এটি ঠিক আছে যে বিশাল ধনাত্মক বা নেতিবাচক মানগুলি পূর্ণসংখ্যক অনির্দিষ্টকালের বিট-প্যাটার্ন থেকে RAX (EAX) শূন্যের নীচের অর্ধেক ছেড়ে যাবে। (ভিন্ন integer-> পূর্ণসংখ্যা ধর্মান্তর, মান মডিউল হ্রাস করা হয় না নিশ্চিত। এআরএম বনাম এক্স 86 উপর স্বাক্ষরবিহীন int- এ সি মান সংজ্ঞায়িত? বিভিন্ন আচরণের ডবল নেতিবাচক ঢালাই আচরণ । পরিষ্কার করা, কোন কিছুই প্রশ্ন অপরিজ্ঞাত বা বাস্তবায়ন-সংজ্ঞায়িত আচরণও I'm আমি কেবল এটাই ইঙ্গিত করছি যে আপনার যদি FP-> int64_t থাকে তবে আপনি এফপি-> uint32_t এ দক্ষতার সাথে প্রয়োগ করতে পারেন এটির মধ্যে x87 রয়েছেfistp যা 32৪-বিট এবং ১--বিট মোডে এমনকি -৪-বিট পূর্ণসংখ্যার গন্তব্য লিখতে পারে, এসএসই 2 নির্দেশাবলীর বিপরীতে যা কেবলমাত্র 64-বিট মোডে 64৪-বিট পূর্ণসংখ্যাকে সরাসরি পরিচালনা করতে পারে।


4
আমি সেই কোডটি সন্ধান করার জন্য প্রলুব্ধ হব তবে ভাগ্যক্রমে আমার এমএসভিসি নেই ...: ডি
অ্যান্টি হাপালা

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