سالیدیتی از اعداد صحیح علامت دار برای استفاده از مقادیر منفی در قراردادهای هوشمند پشتیبانی میکند. این مقاله نحوه استفاده از اعداد صحیح علامت دار در سالیدیتی را در سطح ماشین مجازی اتریوم (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 2 3 |
int8(0) == 0000 0000 type(int8).max == 0111 1111 type(int8).min == 1000 000 |
1 2 |
int8(1) == 0000 0001 int8(-1) == 1111 1111 |
1 2 3 4 |
int8(-2) == 1111 1110 int8(-3) == 1111 1101 int8(-4) == 1111 1100 int8(-5) == 1111 1011 |
یکی از مزایای مهم این روش نمایش، امکان انجام عملیات ریاضی ساده، مانند جمع، به شکل طبیعی و دقیق است. به عنوان مثال، جمع -2
با -2
باید برابر با -4
شود. این نتیجه در سیستم مکمل دوگانه با در نظر گرفتن سرریز بیتها (overflow) حاصل میشود.
برای درک بهتر، این محاسبه را در زبان پایتون مشاهده میکنیم:
1 2 3 4 |
>>> (int(b'11111110', 2) + int(b'11111110', 2) ) % 256 252 >>> bin(252) '0b11111100' |
11111100
است که همان نمایش عدد -4
در int8
محسوب میشود.
اکنون بیایید مقدار -2
را با عدد مثبت +4
جمع کنیم. انتظار میرود نتیجه برابر با +2
باشد:
1 2 3 |
>>> # -2 + 4 >>> (int(b'11111110', 2) + int(b'00000100', 2)) % 256 >>> 2 |
همانطور که مشاهده میشود، نتیجه دقیقاً عدد ۲ است.
این عملیات تنها در صورتی به درستی عمل میکند که هر دو عدد در قالب مکمل دوگانه ذخیره شده باشند. به همین دلیل، زبان سالیدیتی اجازه نمیدهد که عددهای علامت دار و بدون علامت را با هم ترکیب و جمع کنید، زیرا در این حالت هدف برنامه نویس بهوضوح مشخص نیست و ممکن است منجر به بروز خطا در منطق برنامه شود.
عملیات ضرب نیز در این سیستم عملکرد صحیحی دارد. برای نمونه، ضرب -2
در -2
باید برابر با +4
باشد، و سیستم مکمل دوگانه این نتیجه را بدون نیاز به منطق اضافهای به درستی محاسبه میکند. توصیه میشود برای اطمینان، محاسبه آن را نیز به روش بالا بررسی کنید.
محدودیت های سیستم مکمل دوگانه در عملیات ریاضی
اگرچه سیستم مکمل دوگانه در بسیاری از عملیات ریاضی عملکرد دقیقی دارد، اما نمیتوان از آن در همه موارد بهصورت مستقیم استفاده کرد.
در این سیستم، عملیاتهایی مانند جمع، تفریق، ضرب، و شیفت به چپ بیتها (<<
) بدون نیاز به تغییرات اضافی قابل استفاده هستند. این عملیاتها در ماشین مجازی اتریوم (EVM) به ترتیب با دستورهای ADD
، SUB
، MUL
و SHL
انجام میشوند. در بخشهای بعدی این آموزش، به طور دقیق بررسی خواهیم کرد که چرا شیفت به چپ در سیستم مکمل دوگانه همچنان به درستی عمل میکند.
با این حال، برنامه نویس نمیتواند برخی عملیاتها را با روشهای معمول برای اعداد علامت دار انجام دهد، زیرا این روشها با ماهیت چنین دادههایی تطابق ندارند. برای اجرای درست این عملیاتها، باید از دستورهای مخصوص ماشین مجازی اتریوم (EVM) استفاده کند. این عملیات شامل ضرب علامت دار، محاسبه باقیمانده (modulo)، شیفت به راست، و تبدیل عدد به نوع داده علامت دار بزرگتر میشوند.
علاوه بر این، عملگرهای مقایسه سنتی نیز در این سیستم نتیجه درستی ارائه نمیدهند. علت این رفتار به نحوه نمایش باینری اعداد در سیستم مکمل دوگانه برمیگردد؛ زیرا در این نمایش، اعداد منفی از نظر بیتی بزرگتر از اعداد مثبت ظاهر میشوند. بنابراین، برنامه نویس باید از دستورهای خاص و منطق متفاوتی برای مقایسه صحیح اعداد علامت دار استفاده کند.
دستورهای ماشین مجازی اتریوم برای محاسبات علامت دار
sdiv
هزینه گس: ۵
دستور SDIV
(مخفف Signed Division) برای تقسیم اعداد صحیح علامت دار استفاده میشود. این دستور در پشتصحنه کدهایی از سالیدیتی بهکار میرود که در ظاهر ساده به نظر میرسند اما در سطح پایین به اجرای دقیق این دستور نیاز دارند.
برای مثال، کدی مانند نمونه زیر در نهایت از SDIV
در EVM استفاده میکند:
1 2 3 4 |
function divide(int256 a, int256 b) public pure returns (int256 quotient) { quotient = a / b; } |
smod
هزینه گس: ۵
از آنجا که در سیستم مکمل دوگانه (Two’s Complement) تقسیم اعداد علامت دار نیاز به دستور مخصوص خود (sdiv
) دارد، منطقی است که برای محاسبه باقیمانده (modulus) نیز دستور مستقلی در نظر گرفته شده باشد.
1 2 3 4 |
function divide(int256 a, int256 b) public pure returns (int256 remainder) { remainder = a % b; } |
slt
و sgt
هزینه گس: ۳
برای مقایسه اندازه (مقدار مطلق) اعداد صحیح علامت دار، ابتدا باید مشخص کنیم که عدد مثبت است یا منفی، و سپس اندازه آنها را با یکدیگر مقایسه کنیم. دستورهای SLT
(کوچکتر بودن) و SGT
(بزرگتر بودن) این دو مرحله را در یک عملیات ترکیبی انجام میدهند.
این دستورها مخصوص مقایسه بین اعداد علامت دار هستند و عملکردی مشابه دستورهای LT
و GT
برای اعداد بدون علامت دارند، اما با در نظر گرفتن علامت عدد.
همچنین، درست مانند مقایسهگرهای اعداد بدون علامت، بهتر است از عملگرهای نابرابری صِرف (<
و >
) به جای <=
و >=
استفاده شود، چرا که در ماشین مجازی اتریوم مصرف گس کمتری دارند و از نظر بهینهسازی هزینه اجرای قرارداد هوشمند مقرونبهصرفهتر هستند.
sar
– شیفت عددی به راست برای اعداد علامت دار
هزینه گس: ۳
دستور SAR
یکی از دستورهای نسبتاً کمکاربرد در ماشین مجازی اتریوم (EVM) محسوب میشود، اما در نتیجه کامپایل کدهای خاصی از سالیدیتی دیده میشود. برای مثال، در کد زیر:
1 2 3 4 5 |
contract SarExample { function main(int256 x, uint256 y) public pure returns (int256 res) { res = x >> y; } } |
1 2 |
uint256 x = 8 >> 2; // x = 2 uint256 y = 4 >> 1; // y = 2 |
1 2 |
int256 x = -8 >> 2; // x = -2 int256 y = -4 >> 1; // y = -2 |
شاید این سوال پیش بیاید که چرا دستور مستقلی برای شیفت به چپ عددی (یعنی SAL
) وجود ندارد.
به مثال زیر توجه کنید:
1 2 |
int256 x = -8 << 2; // x = -32 int256 y = -4 << 1; // y = -8 |
در این مثالها، اعداد منفی به ترتیب در ۴ و ۲ ضرب شدهاند. در سیستم مکمل دوگانه (Two’s Complement)، شیفت به چپ بهدرستی مقدار عدد را حفظ میکند و بنابراین نیازی به دستور مجزا برای آن وجود ندارد.
ماشین مجازی اتریوم نیز از همان دستور SHL
(شیفت به چپ) برای تمام حالتها استفاده مینماید؛ صرفنظر از اینکه عدد علامت دار باشد یا بدون علامت. اگرچه ممکن است در نگاه اول این رفتار کمی غیرمنتظره به نظر برسد، زیرا در فرآیند شیفت به چپ، بیتهای سمت راست با صفر جایگزین میشوند و مقدار عدد بزرگتر میشود.
با این حال، باید به این نکته توجه داشت که در سیستم مکمل دوگانه، بیشترین مقدار منفی زمانی بهدست میآید که فقط بیت سمت چپ عدد یک باشد و تمامی بیتهای دیگر صفر باشند.
signextend
در ماشین مجازی اتریوم (EVM)
هزینه گس: ۵
وقتی برنامه نویس یک عدد صحیح علامت دار (signed integer) کوچکتر از ۲۵۶ بیت تعریف میکند، سیستم بهطور پیشفرض بیتهای سمت چپ آن را با صفر پر میکند. اما در سیستم مکمل دوگانه (Two’s Complement)، اعداد منفی همیشه با یک در بیت سمت چپ شروع میشوند. اگر برنامه نویس چنین عددی را به نوع بزرگتری تبدیل (cast) کند، سیستم ممکن است آن را به اشتباه یک مقدار مثبت تفسیر کند، زیرا بیتهای سمت چپ در تبدیل، با صفر جایگزین میشوند.
دستور signextend
این مشکل را بهدرستی مدیریت میکند. این دستور اطمینان میدهد که هنگام تبدیل عدد از نوع کوچکتر به بزرگتر، علامت عدد حفظ شود و مقدار آن تغییر نکند.
signextend
در سالیدیتی
در زبان برنامه نویسی سالیدیتی نمیتوان مستقیماً از signextend
استفاده کرد، اما این دستور بهصورت پنهانی در فرآیند کامپایل زمانی استفاده میشود که یک عدد صحیح کوچکتر (مثلاً int8
) به عدد بزرگتر (مثل int256
) تبدیل میشود.
مثال زیر را در نظر بگیرید:
1 2 3 4 5 |
contract SignExtendExample { function main(int8 x) public pure returns (int256 res) { res = x; } } |
راستی! برای دریافت مطالب جدید در کانال تلگرام یا پیج اینستاگرام سورس باران عضو شوید.
- انتشار: ۳ خرداد ۱۴۰۴
دسته بندی موضوعات
- آموزش ارز دیجیتال
- آموزش برنامه نویسی
- آموزش متنی برنامه نویسی
- اطلاعیه و سایر مطالب
- پروژه برنامه نویسی
- دوره های تخصصی برنامه نویسی
- رپورتاژ
- فیلم های آموزشی
- ++C
- ADO.NET
- Adobe Flash
- Ajax
- AngularJS
- apache
- ARM
- Asp.Net
- ASP.NET MVC
- AVR
- Bootstrap
- CCNA
- CCNP
- CMD
- CSS
- Dreameaver
- EntityFramework
- HTML
- IOS
- jquery
- Linq
- Mysql
- Oracle
- PHP
- PHPMyAdmin
- Rational Rose
- silver light
- SQL Server
- Stimulsoft Reports
- Telerik
- UML
- VB.NET&VB6
- WPF
- Xml
- آموزش های پروژه محور
- اتوکد
- الگوریتم تقریبی
- امنیت
- اندروید
- اندروید استودیو
- بک ترک
- بیسیک فور اندروید
- پایتون
- جاوا
- جاوا اسکریپت
- جوملا
- دلفی
- دوره آموزش Go
- دوره های رایگان پیشنهادی
- زامارین
- سئو
- ساخت CMS
- سی شارپ
- شبکه و مجازی سازی
- طراحی الگوریتم
- طراحی بازی
- طراحی وب
- فتوشاپ
- فریم ورک codeigniter
- فلاتر
- کانستراکت
- کریستال ریپورت
- لاراول
- معماری کامپیوتر
- مهندسی اینترنت
- هوش مصنوعی
- یونیتی
- کتاب های آموزشی
- Android
- ASP.NET
- AVR
- LINQ
- php
- Workflow
- اچ تی ام ال
- بانک اطلاعاتی
- برنامه نویسی سوکت
- برنامه نویسی موبایل
- پاسکال
- پایان نامه
- پایتون
- جاوا
- جاوا اسکریپت
- جی کوئری
- داده کاوی
- دلفی
- رباتیک
- سئو
- سایر کتاب ها
- سخت افزار
- سی اس اس
- سی پلاس پلاس
- سی شارپ
- طراحی الگوریتم
- فتوشاپ
- مقاله
- مهندسی نرم افزار
- هک و امنیت
- هوش مصنوعی
- ویژوال بیسیک
- نرم افزار و ابزار برنامه نویسی
- وردپرس