اعداد صحیح علامت دار در سالیدیتی

سالیدیتی از اعداد صحیح علامت دار برای استفاده از مقادیر منفی در قراردادهای هوشمند پشتیبانی می‌کند. این مقاله نحوه استفاده از اعداد صحیح علامت دار در سالیدیتی را در سطح ماشین مجازی اتریوم (EVM) توضیح می‌دهد. آشنایی مقدماتی با EVM و اعداد باینری در اینجا مفروض در نظر گرفته شده است.

توضیح سیستم مکمل دوگانه در سالیدیتی

سالیدیتی و ماشین مجازی اتریوم برای نمایش اعداد صحیح علامت دار از سیستم مکمل دوگانه (Two’s Complement) استفاده می‌کنند.

همانند سایر انواع داده، سالیدیتی برای نمایش اعداد صحیح علامت دار همچنان از کلمات ۳۲ بایتی استفاده می‌کند. در سطح ماشین مجازی، هیچ نشانه معنایی وجود ندارد که مشخص کند یک اسلات ۳۲ بایتی واقعاً مربوط به یک بولین، یک آدرس یا عدد ۱۶۰ بیتی است. این مقدار در زمان کامپایل به عنوان یک عدد منفی تفسیر می‌شود.

با استفاده از عبارت type(int256).max می‌توانید حداکثر مقدار ممکن برای یک عدد صحیح علامت دار را به دست بیاورید، یا با .min حداقل مقدار آن را استخراج کنید. برای تشخیص مثبت یا منفی بودن عدد، یک بیت اضافه نیاز است؛ به همین دلیل، بیشترین مقداری که می‌توان ذخیره کرد، نسبت به نسخه بدون علامت (unsigned) یک بیت کمتر خواهد بود.

اگر سیستم مکمل یک (One’s Complement) استفاده می‌شد، uint256 به uint255 تبدیل می‌شد و بیت سمت چپ مشخص می‌کرد که عدد مثبت است یا منفی. در آن حالت، مقدار type(int256).max دقیقاً با قدر مطلق type(int256).min برابر می‌شد. اما در سیستم مکمل دوگانه این‌گونه نیست. در این سیستم، بیشینه مقدار منفی یک واحد بیشتر از بیشینه مقدار مثبت است. به عنوان مثال، بیشترین عدد مثبت برای int8 برابر با ۱۲۷ است، اما عدد منفی با بزرگ‌ترین اندازه برابر با ۱۲۸- می‌باشد.

الگو و مثال هایی از حساب مکمل دوگانه (Two’s Complement)

به جای اینکه وارد اثبات های ریاضی پیچیده بشویم، بیایید چند مثال ساده و کاربردی را بررسی کنیم. (توجه داشته باشید که هدف این بخش آموزش اصول پایه‌ای مکمل دوگانه است، نه ارائه اثبات رسمی. اگر مایل باشید، منابع زیادی برای مطالعه عمیق‌تر وجود دارد.)

برای ساده‌تر شدن مثال‌ها، از نوع داده int8 استفاده می‌کنیم. یعنی عدد صحیح ۸ بیتی که هم می‌تواند مقدار مثبت داشته باشد و هم منفی. در این مثال‌ها از نمایش باینری استفاده شده، یعنی هر عدد را به‌صورت صفر و یک نمایش می‌دهیم، نه به صورت عدد معمولی.

حالا بیایید ببینیم عدد +1 و -1 در این سیستم چطور نمایش داده می‌شوند:
اگر به صورت پیوسته مقادیر منفی را کاهش دهیم، یک الگوی مشخص در بیت‌ها مشاهده می‌شود:
در واقع می‌توان گفت سیستم مکمل دوگانه، اعداد منفی را به صورت یک شمارش معکوس در قالب باینری نمایش می‌دهد.

یکی از مزایای مهم این روش نمایش، امکان انجام عملیات ریاضی ساده، مانند جمع، به شکل طبیعی و دقیق است. به عنوان مثال، جمع -2 با -2 باید برابر با -4 شود. این نتیجه در سیستم مکمل دوگانه با در نظر گرفتن سرریز بیت‌ها (overflow) حاصل می‌شود.

برای درک بهتر، این محاسبه را در زبان پایتون مشاهده می‌کنیم:

عدد ۲۵۲ در مبنای باینری برابر با 11111100 است که همان نمایش عدد -4 در int8 محسوب می‌شود.

اکنون بیایید مقدار -2 را با عدد مثبت +4 جمع کنیم. انتظار می‌رود نتیجه برابر با +2 باشد:

همان‌طور که مشاهده می‌شود، نتیجه دقیقاً عدد ۲ است.

این عملیات تنها در صورتی به درستی عمل می‌کند که هر دو عدد در قالب مکمل دوگانه ذخیره شده باشند. به همین دلیل، زبان سالیدیتی اجازه نمی‌دهد که عددهای علامت دار و بدون علامت را با هم ترکیب و جمع کنید، زیرا در این حالت هدف برنامه نویس به‌وضوح مشخص نیست و ممکن است منجر به بروز خطا در منطق برنامه شود.

عملیات ضرب نیز در این سیستم عملکرد صحیحی دارد. برای نمونه، ضرب -2 در -2 باید برابر با +4 باشد، و سیستم مکمل دوگانه این نتیجه را بدون نیاز به منطق اضافه‌ای به درستی محاسبه می‌کند. توصیه می‌شود برای اطمینان، محاسبه آن را نیز به روش بالا بررسی کنید.

محدودیت های سیستم مکمل دوگانه در عملیات ریاضی

اگرچه سیستم مکمل دوگانه در بسیاری از عملیات ریاضی عملکرد دقیقی دارد، اما نمی‌توان از آن در همه موارد به‌صورت مستقیم استفاده کرد.

در این سیستم، عملیات‌هایی مانند جمع، تفریق، ضرب، و شیفت به چپ بیت‌ها (<<) بدون نیاز به تغییرات اضافی قابل استفاده هستند. این عملیات‌ها در ماشین مجازی اتریوم (EVM) به ترتیب با دستورهای ADD، SUB، MUL و SHL انجام می‌شوند. در بخش‌های بعدی این آموزش، به طور دقیق بررسی خواهیم کرد که چرا شیفت به چپ در سیستم مکمل دوگانه همچنان به درستی عمل می‌کند.

با این حال، برنامه نویس نمی‌تواند برخی عملیات‌ها را با روش‌های معمول برای اعداد علامت دار انجام دهد، زیرا این روش‌ها با ماهیت چنین داده‌هایی تطابق ندارند. برای اجرای درست این عملیات‌ها، باید از دستورهای مخصوص ماشین مجازی اتریوم (EVM) استفاده کند. این عملیات شامل ضرب علامت دار، محاسبه باقیمانده (modulo)، شیفت به راست، و تبدیل عدد به نوع داده علامت دار بزرگ‌تر می‌شوند.

علاوه بر این، عملگرهای مقایسه سنتی نیز در این سیستم نتیجه درستی ارائه نمی‌دهند. علت این رفتار به نحوه نمایش باینری اعداد در سیستم مکمل دوگانه برمی‌گردد؛ زیرا در این نمایش، اعداد منفی از نظر بیتی بزرگ‌تر از اعداد مثبت ظاهر می‌شوند. بنابراین، برنامه نویس باید از دستورهای خاص و منطق متفاوتی برای مقایسه صحیح اعداد علامت دار استفاده کند.

دستورهای ماشین مجازی اتریوم برای محاسبات علامت دار

sdiv

هزینه گس: ۵

دستور SDIV (مخفف Signed Division) برای تقسیم اعداد صحیح علامت دار استفاده می‌شود. این دستور در پشت‌صحنه کدهایی از سالیدیتی به‌کار می‌رود که در ظاهر ساده به نظر می‌رسند اما در سطح پایین به اجرای دقیق این دستور نیاز دارند.

برای مثال، کدی مانند نمونه زیر در نهایت از SDIV در EVM استفاده می‌کند:

smod

هزینه گس: ۵

از آن‌جا که در سیستم مکمل دوگانه (Two’s Complement) تقسیم اعداد علامت دار نیاز به دستور مخصوص خود (sdiv) دارد، منطقی است که برای محاسبه باقیمانده (modulus) نیز دستور مستقلی در نظر گرفته شده باشد.

slt و sgt

هزینه گس: ۳

برای مقایسه اندازه (مقدار مطلق) اعداد صحیح علامت دار، ابتدا باید مشخص کنیم که عدد مثبت است یا منفی، و سپس اندازه آن‌ها را با یکدیگر مقایسه کنیم. دستورهای SLT (کوچک‌تر بودن) و SGT (بزرگ‌تر بودن) این دو مرحله را در یک عملیات ترکیبی انجام می‌دهند.

این دستورها مخصوص مقایسه بین اعداد علامت دار هستند و عملکردی مشابه دستورهای LT و GT برای اعداد بدون علامت دارند، اما با در نظر گرفتن علامت عدد.

همچنین، درست مانند مقایسه‌گرهای اعداد بدون علامت، بهتر است از عملگرهای نابرابری صِرف (< و >) به جای <= و >= استفاده شود، چرا که در ماشین مجازی اتریوم مصرف گس کمتری دارند و از نظر بهینه‌سازی هزینه اجرای قرارداد هوشمند مقرون‌به‌صرفه‌تر هستند.

sar – شیفت عددی به راست برای اعداد علامت دار

هزینه گس: ۳

دستور SAR یکی از دستورهای نسبتاً کم‌کاربرد در ماشین مجازی اتریوم (EVM) محسوب می‌شود، اما در نتیجه کامپایل کدهای خاصی از سالیدیتی دیده می‌شود. برای مثال، در کد زیر:

در مورد اعداد بدون علامت، هر بار که بیت‌ها را یک واحد به سمت راست شیفت می‌دهیم، عملاً عدد را بر ۲ تقسیم می‌کنیم. اگر دو بیت شیفت بدهیم، عدد بر ۴ تقسیم می‌شود، و به همین ترتیب:
در مورد اعداد علامت دار نیز همین الگو حفظ می‌شود و رفتار مشابهی را شاهد هستیم:
در این حالت نیز عدد منفی به شکل صحیح بر ۲ یا ۴ تقسیم می‌شود و به پایین‌ترین عدد صحیح گرد می‌شود.

شاید این سوال پیش بیاید که چرا دستور مستقلی برای شیفت به چپ عددی (یعنی SAL) وجود ندارد.

به مثال زیر توجه کنید:

در این مثال‌ها، اعداد منفی به ترتیب در ۴ و ۲ ضرب شده‌اند. در سیستم مکمل دوگانه (Two’s Complement)، شیفت به چپ به‌درستی مقدار عدد را حفظ می‌کند و بنابراین نیازی به دستور مجزا برای آن وجود ندارد.

ماشین مجازی اتریوم نیز از همان دستور SHL (شیفت به چپ) برای تمام حالت‌ها استفاده می‌نماید؛ صرف‌نظر از اینکه عدد علامت دار باشد یا بدون علامت. اگرچه ممکن است در نگاه اول این رفتار کمی غیرمنتظره به نظر برسد، زیرا در فرآیند شیفت به چپ، بیت‌های سمت راست با صفر جایگزین می‌شوند و مقدار عدد بزرگ‌تر می‌شود.

با این حال، باید به این نکته توجه داشت که در سیستم مکمل دوگانه، بیشترین مقدار منفی زمانی به‌دست می‌آید که فقط بیت سمت چپ عدد یک باشد و تمامی بیت‌های دیگر صفر باشند.

signextend در ماشین مجازی اتریوم (EVM)

هزینه گس: ۵

وقتی برنامه نویس یک عدد صحیح علامت دار (signed integer) کوچک‌تر از ۲۵۶ بیت تعریف می‌کند، سیستم به‌طور پیش‌فرض بیت‌های سمت چپ آن را با صفر پر می‌کند. اما در سیستم مکمل دوگانه (Two’s Complement)، اعداد منفی همیشه با یک در بیت سمت چپ شروع می‌شوند. اگر برنامه نویس چنین عددی را به نوع بزرگ‌تری تبدیل (cast) کند، سیستم ممکن است آن را به اشتباه یک مقدار مثبت تفسیر کند، زیرا بیت‌های سمت چپ در تبدیل، با صفر جایگزین می‌شوند.

دستور signextend این مشکل را به‌درستی مدیریت می‌کند. این دستور اطمینان می‌دهد که هنگام تبدیل عدد از نوع کوچک‌تر به بزرگ‌تر، علامت عدد حفظ شود و مقدار آن تغییر نکند.

signextend در سالیدیتی

در زبان برنامه نویسی سالیدیتی نمی‌توان مستقیماً از signextend استفاده کرد، اما این دستور به‌صورت پنهانی در فرآیند کامپایل زمانی استفاده می‌شود که یک عدد صحیح کوچک‌تر (مثلاً int8) به عدد بزرگ‌تر (مثل int256) تبدیل می‌شود.

مثال زیر را در نظر بگیرید:

نکته مهم این است که برعکس این تبدیل ممکن نیست: نمی‌توان عددهای بزرگ‌تر را به نوع‌های کوچک‌تر تبدیل کرد، چون ممکن است اطلاعات مهم (از جمله علامت یا مقدار عدد) از بین برود.

5/5 - (1 امتیاز)

راستی! برای دریافت مطالب جدید در کانال تلگرام یا پیج اینستاگرام سورس باران عضو شوید.

پکیج جامع و پروژه محور ASP.NET MVC + طراحی فروشگاه اینترنتی فروش فایل
  • انتشار: ۳ خرداد ۱۴۰۴

دسته بندی موضوعات

آخرین محصولات فروشگاه

مشاهده همه

نظرات

بازخوردهای خود را برای ما ارسال کنید