কিউএনএএন এবং এসএনএএন পরীক্ষামূলকভাবে দেখতে কেমন?
আসুন প্রথমে শিখি কীভাবে সনাক্ত করতে হয় আমাদের কাছে একটি এসএনএএন বা কিউএনএন রয়েছে।
আমি সি এর পরিবর্তে এই উত্তরে সি ++ ব্যবহার করব কারণ এটি সুবিধাজনক অফার করে std::numeric_limits::quiet_NaN
এবং std::numeric_limits::signaling_NaN
যা আমি সি তে সুবিধাজনকভাবে খুঁজে পাই না।
তবে কোনও NaN এসএনএন বা কিউএনএন হলে শ্রেণিবদ্ধ করার জন্য আমি কোনও ফাংশনটি খুঁজে পাইনি, সুতরাং আসুন আমরা কেবলমাত্র NaN কাঁচা বাইটগুলি প্রিন্ট আউট করব:
main.cpp
#include <cassert>
#include <cstring>
#include <cmath> // nanf, isnan
#include <iostream>
#include <limits> // std::numeric_limits
#pragma STDC FENV_ACCESS ON
void print_float(float f) {
std::uint32_t i;
std::memcpy(&i, &f, sizeof f);
std::cout << std::hex << i << std::endl;
}
int main() {
static_assert(std::numeric_limits<float>::has_quiet_NaN, "");
static_assert(std::numeric_limits<float>::has_signaling_NaN, "");
static_assert(std::numeric_limits<float>::has_infinity, "");
// Generate them.
float qnan = std::numeric_limits<float>::quiet_NaN();
float snan = std::numeric_limits<float>::signaling_NaN();
float inf = std::numeric_limits<float>::infinity();
float nan0 = std::nanf("0");
float nan1 = std::nanf("1");
float nan2 = std::nanf("2");
float div_0_0 = 0.0f / 0.0f;
float sqrt_negative = std::sqrt(-1.0f);
// Print their bytes.
std::cout << "qnan "; print_float(qnan);
std::cout << "snan "; print_float(snan);
std::cout << " inf "; print_float(inf);
std::cout << "-inf "; print_float(-inf);
std::cout << "nan0 "; print_float(nan0);
std::cout << "nan1 "; print_float(nan1);
std::cout << "nan2 "; print_float(nan2);
std::cout << " 0/0 "; print_float(div_0_0);
std::cout << "sqrt "; print_float(sqrt_negative);
// Assert if they are NaN or not.
assert(std::isnan(qnan));
assert(std::isnan(snan));
assert(!std::isnan(inf));
assert(!std::isnan(-inf));
assert(std::isnan(nan0));
assert(std::isnan(nan1));
assert(std::isnan(nan2));
assert(std::isnan(div_0_0));
assert(std::isnan(sqrt_negative));
}
সংকলন এবং চালান:
g++ -ggdb3 -O3 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out
আমার x86_64 মেশিনে আউটপুট:
qnan 7fc00000
snan 7fa00000
inf 7f800000
-inf ff800000
nan0 7fc00000
nan1 7fc00001
nan2 7fc00002
0/0 ffc00000
sqrt ffc00000
আমরা কিউএমইউ ব্যবহারকারীর মোডের সাহায্যে আর্চ 64 এ প্রোগ্রামটি চালাতে পারি:
aarch64-linux-gnu-g++ -ggdb3 -O3 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
qemu-aarch64 -L /usr/aarch64-linux-gnu/ main.out
এবং এটি হুবহু একই আউটপুট উত্পাদন করে যে একাধিক খিলান ঘনিষ্ঠভাবে আইইইই 754 বাস্তবায়ন পরামর্শ দেয়।
এই মুহুর্তে, আপনি যদি আইইইই 754 ভাসমান পয়েন্ট সংখ্যাগুলির কাঠামোর সাথে পরিচিত না হন তবে একবার নজর দিন: একটি সাধারণ অস্থায়ী পয়েন্ট সংখ্যাটি কী?
বাইনারি উপরের কিছু মান রয়েছে:
31
|
| 30 23 22 0
| | | | |
-----+-+------+-+---------------------+
qnan 0 11111111 10000000000000000000000
snan 0 11111111 01000000000000000000000
inf 0 11111111 00000000000000000000000
-inf 1 11111111 00000000000000000000000
-----+-+------+-+---------------------+
| | | | |
| +------+ +---------------------+
| | |
| v v
| exponent fraction
|
v
sign
এই পরীক্ষা থেকে আমরা পর্যবেক্ষণ করি:
qNaN এবং sNaN 22 বিট দ্বারা কেবল পৃথক বলে মনে হচ্ছে শান্ত 1, এবং 0 এর অর্থ সংকেত
ইনফিনিটিগুলি = 0 0xFF এর সাথেও বেশ মিল, তবে তাদের ভগ্নাংশ == 0 রয়েছে।
এই কারণে, NaNs 21 থেকে 1 বিট সেট করতে হবে, অন্যথায় sNaN কে ইতিবাচক অনন্ত থেকে আলাদা করা সম্ভব হবে না!
nanf()
বিভিন্ন বিভিন্ন NaN উত্পাদন করে, তাই একাধিক সম্ভাব্য এনকোডিং অবশ্যই থাকতে হবে:
7fc00000
7fc00001
7fc00002
যেহেতু nan0
একই std::numeric_limits<float>::quiet_NaN()
, তাই আমরা অনুমান করি যে তারা সবাই আলাদা আলাদা শান্ত এনএন।
C11 N1570 মান খসড়া নিশ্চিত করে যে nanf()
শান্ত Nans উত্পন্ন, কারণ nanf
সামনে থেকে strtod
এবং 7.22.1.3 "strtod, strtof এবং strtold ফাংশন" বলেছেন:
একটি অক্ষর অনুক্রম এনএএন বা এনএএন (এন-চর-সিকোয়েন্স অপ্ট )টিকে একটি শান্ত নেএন হিসাবে ব্যাখ্যা করা হয়, যদি রিটার্ন টাইপের ক্ষেত্রে সমর্থন করা হয়, অন্যথায় বিষয় সিকোয়েন্স অংশের মতো যার প্রত্যাশিত ফর্ম নেই; এন-চর ক্রমের অর্থ বাস্তবায়ন-সংজ্ঞায়িত। 293)
আরো দেখুন:
ম্যানুয়ালগুলিতে qNaN এবং sNaN দেখতে কেমন?
আইইইই 754 2008 সুপারিশ করে যে (TODO বাধ্যতামূলক বা alচ্ছিক?):
- কাফের == 0xFF এবং ভগ্নাংশ সহ যে কোনও কিছুই! = 0 একটি NaN
- এবং সর্বাধিক ভগ্নাংশ বিট এসএনএএন থেকে কিউএনএনকে পার্থক্য করে
তবে এটি এনএএন থেকে অনন্তের পার্থক্যের তুলনায় কোন বিটকে বেশি পছন্দ করে তা বলে মনে হচ্ছে না।
6.2.1 "বাইনারি ফর্ম্যাটগুলিতে NaN এনকোডিংস" বলেছেন:
এই সাবক্লেজটি NaNs এর এনকোডিংগুলিকে বিট স্ট্রিং হিসাবে নির্দিষ্ট করে যখন তারা অপারেশনের ফলাফল হয়। এনকোড করা অবস্থায়, সমস্ত এনএএন-এর এনএনড হিসাবে এনএনডিং সনাক্তকরণের জন্য প্রয়োজনীয় একটি চিহ্ন বিট এবং বিটের একটি প্যাটার্ন থাকে এবং এটি তার ধরণের (এসএনএএনএন বনাম কিউএনএন) নির্ধারণ করে। বাকি বিটগুলি, যা পিছনে তাত্পর্যপূর্ণ ক্ষেত্রের মধ্যে রয়েছে, পে-লোডকে এনকোড করে দেয় যা ডায়াগনস্টিক তথ্য হতে পারে (উপরে দেখুন)। 34
সমস্ত বাইনারি ন্যান বিট স্ট্রিংগুলিতে পক্ষপাতদুষ্ট এক্সপোনেন্ট ফিল্ডের সমস্ত বিট 1 সেট করা আছে (দেখুন 3.4)। একটি নিখুঁত এনএইন বিট স্ট্রিংটি অনুগামী তাত্পর্যপূর্ণ ক্ষেত্র টি এর প্রথম বিট (ডি 1) এর সাথে এনকোড করা উচিত 1. অনুচরনের তাত্পর্যপূর্ণ ক্ষেত্রটি 0 হয়, অনুগ্রহ থেকে NaN পার্থক্য করার জন্য অনুসরণযোগ্য তাত্পর্য ক্ষেত্রের অন্য কিছু বিট অবশ্যই শূন্য নয়। স্রেফ বর্ণিত পছন্দের এনকোডিং-এ, ডি -1 থেকে 1 সেট করে একটি সিগন্যালিং এনএএন শান্ত করা হবে, টি-র অবশিষ্ট বিটগুলি অপরিবর্তিত রেখে। বাইনারি ফর্ম্যাটগুলির জন্য, পে-লোড পি-enc 2 টি অনুমানের ক্ষেত্রের তাত্পর্যপূর্ণ ক্ষেত্রের কমপক্ষে উল্লেখযোগ্য বিটগুলিতে এনকোড থাকে
ইন্টেল 64 এবং IA-32 আর্কিটেকচারের সফটওয়্যার উন্নয়নকারীর ম্যানুয়াল - ভলিউম 1 বেসিক আর্কিটেকচার - 253665-056US সেপ্টেম্বর 2015 4.8.3.4 "Nans" নিশ্চিত করে যে এক্স 86 সর্বোচ্চ ভগ্নাংশ অল্পে NaN এবং sNaN পার্থক্য দ্বারা আইইইই 754 অনুসরণ করে:
আইএ -32 আর্কিটেকচার NaNs এর দুটি শ্রেণি সংজ্ঞায়িত করে: শান্ত NaNs (QNaNs) এবং NaNs (SNaNs) সিগন্যাল করে। একটি কিউএনএএন হ'ল একটি এনএএন যা সবচেয়ে উল্লেখযোগ্য ভগ্নাংশ বিট সেট করে একটি এসএনএনএন একটি এনএএনএন যেখানে সবচেয়ে উল্লেখযোগ্য ভগ্নাংশ বিট পরিষ্কার হয়।
এবং তাই এআরএম আর্কিটেকচার রেফারেন্স ম্যানুয়াল - এআরএমভি 8, এআরএমভি 8-একটি আর্কিটেকচার প্রোফাইলের জন্য - ডিডিআই 0487C.a এ 1.4.3 "একক-নির্ভুলতা ভাসমান-বিন্যাস ফর্ম্যাট":
fraction != 0
: মানটি একটি NaN, এবং হয় শান্ত NaN বা সিগন্যালিং এনএএন। দুই ধরণের NaN তাদের উল্লেখযোগ্য ভগ্নাংশ বিট, বিট [22] দ্বারা পৃথক করা হয়েছে:
bit[22] == 0
: এনএএন একটি সিগন্যালিং এনএন N সাইন বিট যে কোনও মান নিতে পারে এবং বাকি ভগ্নাংশ বিটগুলি সমস্ত শূন্য ছাড়া কোনও মান নিতে পারে।
bit[22] == 1
: দ্য এনএন একটি শান্ত ন্যান। সাইন বিট এবং অবশিষ্ট ভগ্নাংশ বিটগুলি কোনও মান নিতে পারে।
কিউএনএনএস এবং এসএনএএনএস কীভাবে উত্পন্ন হয়?
QNaN এবং sNaN এর মধ্যে একটি প্রধান পার্থক্য হ'ল:
- qNaN অদ্ভুত মান সহ নিয়মিত অন্তর্নির্মিত (সফ্টওয়্যার বা হার্ডওয়্যার) গাণিতিক ক্রিয়াকলাপ দ্বারা উত্পাদিত হয়
- এসএনএএন কখনই অন্তর্নির্মিত ক্রিয়াকলাপের মাধ্যমে উত্পন্ন হয় না, এটি কেবল প্রোগ্রামারদের দ্বারা স্পষ্টভাবে যুক্ত করা যেতে পারে, যেমন
std::numeric_limits::signaling_NaN
আমি এর জন্য স্পষ্ট আইইইই 754 বা সি 11 উদ্ধৃতি খুঁজে পাইনি, তবে sNaNs জেনারেট করে এমন কোনও বিল্ট-ইন অপারেশনও খুঁজে পাচ্ছি না ;-)
ইন্টেল ম্যানুয়ালটি 4.8.3.4 "NaNs" এ এই নীতিটি পরিষ্কারভাবে জানিয়েছে:
এসএনএএনগুলি সাধারণত একটি ব্যতিক্রম হ্যান্ডলারকে ফাঁদে ফেলতে বা অনুরোধ করতে ব্যবহৃত হয়। সেগুলি অবশ্যই সফ্টওয়্যার দ্বারা প্রবেশ করানো উচিত; অর্থাৎ, প্রসেসর কোনও ভাসমান-পয়েন্ট অপারেশনের ফলস্বরূপ কোনও এসএনএএন জেনারেট করে না।
এটি আমাদের উদাহরণ থেকে দেখা যায় যেখানে উভয়ই:
float div_0_0 = 0.0f / 0.0f;
float sqrt_negative = std::sqrt(-1.0f);
ঠিক একই বিট উত্পাদন std::numeric_limits<float>::quiet_NaN()
।
এই দুটি অপারেশনই সিঙ্গেল x86 এ্যাসেম্বলি নির্দেশের সাথে সংকলন করে যা হার্ডওয়ারে সরাসরি QNaN উৎপন্ন করে (টোডো জিডিবি দিয়ে কনফার্ম করে)।
কিউএনএএন এবং এসএনএএন আলাদাভাবে কী করে?
এখন যেহেতু আমরা জানি যে কীএনএএন এবং এসএনএএনগুলি দেখতে কীভাবে, এবং কীভাবে সেগুলি চালিত করতে হয়, আমরা শেষ পর্যন্ত এসএনএএনগুলি তাদের জিনিসটি করার চেষ্টা করতে এবং কিছু প্রোগ্রাম উড়িয়ে দেওয়ার জন্য প্রস্তুত!
সুতরাং আরও অ্যাডো ছাড়া:
blow_up.cpp
#include <cassert>
#include <cfenv>
#include <cmath> // isnan
#include <iostream>
#include <limits> // std::numeric_limits
#include <unistd.h>
#pragma STDC FENV_ACCESS ON
int main() {
float snan = std::numeric_limits<float>::signaling_NaN();
float qnan = std::numeric_limits<float>::quiet_NaN();
float f;
// No exceptions.
assert(std::fetestexcept(FE_ALL_EXCEPT) == 0);
// Still no exceptions because qNaN.
f = qnan + 1.0f;
assert(std::isnan(f));
if (std::fetestexcept(FE_ALL_EXCEPT) == FE_INVALID)
std::cout << "FE_ALL_EXCEPT qnan + 1.0f" << std::endl;
// Now we can get an exception because sNaN, but signals are disabled.
f = snan + 1.0f;
assert(std::isnan(f));
if (std::fetestexcept(FE_ALL_EXCEPT) == FE_INVALID)
std::cout << "FE_ALL_EXCEPT snan + 1.0f" << std::endl;
feclearexcept(FE_ALL_EXCEPT);
// And now we enable signals and blow up with SIGFPE! >:-)
feenableexcept(FE_INVALID);
f = qnan + 1.0f;
std::cout << "feenableexcept qnan + 1.0f" << std::endl;
f = snan + 1.0f;
std::cout << "feenableexcept snan + 1.0f" << std::endl;
}
সংকলন করুন, চালান এবং প্রস্থান স্থিতি পান:
g++ -ggdb3 -O0 -Wall -Wextra -pthread -std=c++11 -pedantic-errors -o blow_up.out blow_up.cpp -lm -lrt
./blow_up.out
echo $?
আউটপুট:
FE_ALL_EXCEPT snan + 1.0f
feenableexcept qnan + 1.0f
Floating point exception (core dumped)
136
নোট করুন যে এই আচরণটি কেবলমাত্র -O0
জিসিসি 8.2 তে ঘটে : এর সাথে -O3
, জিসিসি আমাদের সমস্ত এসএনএএন অপারেশনকে প্রাক-গণনা করে এবং অনুকূলিত করে! আমি এটি নিশ্চিত না যে এটির প্রতিরোধের কোনও মানক উপায় আছে কিনা।
সুতরাং আমরা এই উদাহরণ থেকে অনুমান যে:
snan + 1.0
কারণ FE_INVALID
, কিন্তু qnan + 1.0
না
লিনাক্স কেবল তখনই সিগন্যাল উত্পন্ন করে যদি এটি সক্ষম হয় feenableexept
।
এটি একটি গ্লাবসি এক্সটেনশন, আমি কোনও মানক হিসাবে এটি করার কোনও উপায় খুঁজে পাইনি।
সিগন্যালটি ঘটলে এটি সিপিইউ হার্ডওয়্যার নিজেই একটি ব্যতিক্রম উত্থাপন করে, যা লিনাক্স কার্নেল সিগন্যালের মাধ্যমে অ্যাপ্লিকেশনটি পরিচালনা ও অবহিত করে।
ফলাফল যে ব্যাশ ছাপে Floating point exception (core dumped)
, এবং প্রস্থান অবস্থা 136
, যা অনুরূপ সংকেত 136 - 128 == 8
, যা অনুযায়ী:
man 7 signal
হয় SIGFPE
।
দ্রষ্টব্য SIGFPE
এটি একই সংকেত যা আমরা পাই যদি একটি পূর্ণসংখ্যা 0 দ্বারা ভাগ করার চেষ্টা করি তবে:
int main() {
int i = 1 / 0;
}
যদিও পূর্ণসংখ্যার জন্য:
- কোনও কিছুর শূন্য দ্বারা ভাগ করা সংকেত উত্থাপন করে, যেহেতু পূর্ণসংখ্যায় কোনও অনন্ত প্রতিনিধিত্ব নেই
- সিগন্যালটি এটি প্রয়োজন ছাড়াই ডিফল্টভাবে ঘটে
feenableexcept
কীভাবে SIGFPE পরিচালনা করবেন?
যদি আপনি কেবল একটি হ্যান্ডলার তৈরি করেন যা সাধারণত ফিরে আসে তবে এটি একটি অসীম লুপকে নিয়ে যায়, কারণ হ্যান্ডলারটি ফিরে আসার পরে বিভাগটি আবার ঘটে! এটি জিডিবি দিয়ে যাচাই করা যেতে পারে।
একমাত্র উপায় হ'ল যেমন দেখানো হয়েছে তেমন ব্যবহার করা setjmp
এবং longjmp
অন্য কোথাও লাফানো: সি হ্যান্ডেল সংকেত SIGFPE এবং কার্যকর করা চালিয়ে যাওয়া execution
এসএনএএন-এর কয়েকটি বাস্তব বিশ্বের অ্যাপ্লিকেশন কী কী?
মোটামুটি সত্যই, আমি এখনও এসএনএএনএসের জন্য একটি দরকারী দরকারী ব্যবহারের বিষয়টি বুঝতে পারি নি, এটিতে এখানে জিজ্ঞাসা করা হয়েছে: এনএএন সিগন্যাল করার উপযোগিতা?
এসএনএএনগুলি বিশেষত অকেজো বলে মনে হয় কারণ আমরা প্রাথমিক অবৈধ ক্রিয়াকলাপগুলি সনাক্ত করতে পারি (যা 0.0f/0.0f
) এর সাথে কিউএনএএন তৈরি করে feenableexcept
: এটি প্রদর্শিত হয় যে snan
আরও বেশি ক্রিয়াকলাপের জন্য ত্রুটি উত্থাপন qnan
করে, যেমন ( qnan + 1.0f
)।
যেমন:
main.c
#define _GNU_SOURCE
#include <fenv.h>
#include <stdio.h>
int main(int argc, char **argv) {
(void)argv;
float f0 = 0.0;
if (argc == 1) {
feenableexcept(FE_INVALID);
}
float f1 = 0.0 / f0;
printf("f1 %f\n", f1);
feenableexcept(FE_INVALID);
float f2 = f1 + 1.0;
printf("f2 %f\n", f2);
}
সংকলন:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -lm
তারপরে:
./main.out
দেয়:
Floating point exception (core dumped)
এবং:
./main.out 1
দেয়:
f1 -nan
f2 -nan
আরও দেখুন: সি ++ এ কীভাবে কোনও NAN ট্রেস করবেন
সিগন্যাল পতাকাগুলি কী কী এবং সেগুলি কীভাবে পরিচালিত হয়?
সবকিছুই সিপিইউ হার্ডওয়্যারের মধ্যে প্রয়োগ করা হয়।
পতাকাগুলি কিছু রেজিস্টারে থাকে এবং তাই কিছুটা বলে যে কোনও ব্যতিক্রম / সংকেত উত্থাপন করা উচিত কিনা।
এই নিবন্ধগুলি বেশিরভাগ আর্চ থেকে ব্যবহারকারীল্যান্ড থেকে অ্যাক্সেসযোগ্য ।
গ্লিবসি ২.২৯ কোডের এই অংশটি আসলে বুঝতে খুব সহজ!
উদাহরণস্বরূপ, fetestexcept
x86_86 এর জন্য sysdeps / x86_64 / fpu / ftestexcept.c এ প্রয়োগ করা হয়েছে :
#include <fenv.h>
int
fetestexcept (int excepts)
{
int temp;
unsigned int mxscr;
/* Get current exceptions. */
__asm__ ("fnstsw %0\n"
"stmxcsr %1" : "=m" (*&temp), "=m" (*&mxscr));
return (temp | mxscr) & excepts & FE_ALL_EXCEPT;
}
libm_hidden_def (fetestexcept)
সুতরাং আমরা তাত্ক্ষণিকভাবে দেখতে পাচ্ছি যে নির্দেশাবলীর ব্যবহারগুলি হ'ল stmxcsr
"স্টোর এমএক্সসিএসআর রেজিস্টার স্টেট"।
এবং sysdeps / x86_64 / fpu / feenablxcpt.cfeenableexcept
এ প্রয়োগ করা হয়েছে :
#include <fenv.h>
int
feenableexcept (int excepts)
{
unsigned short int new_exc, old_exc;
unsigned int new;
excepts &= FE_ALL_EXCEPT;
/* Get the current control word of the x87 FPU. */
__asm__ ("fstcw %0" : "=m" (*&new_exc));
old_exc = (~new_exc) & FE_ALL_EXCEPT;
new_exc &= ~excepts;
__asm__ ("fldcw %0" : : "m" (*&new_exc));
/* And now the same for the SSE MXCSR register. */
__asm__ ("stmxcsr %0" : "=m" (*&new));
/* The SSE exception masks are shifted by 7 bits. */
new &= ~(excepts << 7);
__asm__ ("ldmxcsr %0" : : "m" (*&new));
return old_exc;
}
সি স্ট্যান্ডার্ড কিউএনএএন বনাম এসএনএএন সম্পর্কে কী বলে?
C11 N1570 মান খসড়া স্পষ্টভাবে বলছেন যে মান F.2.1 "অসীমতার সাইনড শূন্য, এবং Nans" তাদের মধ্যে পার্থক্য করে না:
1 এই স্পেসিফিকেশন NaN গুলি সিগন্যাল করার আচরণ সংজ্ঞায়িত করে না। এটি শান্ত NaNs বোঝাতে সাধারণত NaN শব্দটি ব্যবহার করে। নন এবং ইনফিনিটি ম্যাক্রোগুলি এবং ন্যান ফাংশনগুলি <math.h>
আইইসি 60559 ন্যান এবং ইনফিনিটিগুলির জন্য উপাধি সরবরাহ করে।
উবুন্টু 18.10, জিসিসি 8.2 তে পরীক্ষিত। গিটহাব উজানের ধারা: