ट्रैक
/
x86-64 Assembly
x86-64 Assembly
/
अभ्यास
/
मुद्रा विनिमय
मुद्रा विनिमय

मुद्रा विनिमय

सीखने का अभ्यास

परिचय

पिछले कॉन्सेप्ट में बताया गया था कि global डायरेक्टिव किसी एक सोर्स फाइल में बनाए गए फंक्शन या वेरिएबल को दूसरी फाइलों के लिए दिखने योग्य बना देता है। इसी तरह, extern डायरेक्टिव असेंबलर को बताता है कि वर्तमान सोर्स फाइल में इस्तेमाल किया गया फंक्शन या वेरिएबल किसी दूसरी फाइल में बनाया गया है।

कोड को मॉड्यूल में बाँटने के लिए एसेंबली कोड दूसरी सोर्स फाइलों के साथ जिस मुख्य तरीके से जुड़ता है, वह ये दोनों डायरेक्टिव हैं।

इन डायरेक्टिव के लिए इससे कोई फर्क नहीं पड़ता कि बाहरी कोड एसेंबली में बनाया या इस्तेमाल किया गया है, या किसी हाई-लेवल भाषा में। इसलिए, उदाहरण के लिए, हाई-लेवल भाषा में बनाए गए फंक्शन को भी x86-64 कोड में इस्तेमाल किया जा सकता है, अगर उसे extern घोषित किया गया हो।

इसी तरह, यह कॉलिंग कन्वेंशन उन सभी फंक्शन के बीच एक जैसा रहता है, चाहे वे एसेंबली में बने हों या हाई-लेवल भाषाओं में, और ऑपरेटिंग सिस्टम (OS) के साथ भी।

लेकिन चूँकि एसेंबली में डेटा बस बाइटों का एक क्रम होता है, इसलिए यह जानना ज़रूरी है कि जिस हाई-लेवल भाषा के साथ हम जुड़ रहे हैं, वह मेमोरी को कैसे संभालती है।

C के टाइप

एसेंबली कोड के साथ इंटरफेस करने के लिए हाई-लेवल भाषा के रूप में C एक आम पसंद है। यह OS के साथ जुड़ना आसान बना देती है और कई ज़रूरी कामों के लिए हाई-लेवल रैपर देती है, जैसे स्क्रीन पर प्रिंट करना, कीबोर्ड से पढ़ना या डायनैमिक मेमोरी आवंटित करना।

इस ट्रैक में टेस्ट C में लिखे गए हैं, इसलिए इस भाषा का एक सामान्य परिचय देना ज़रूरी है।

Note

नीचे बताए गए किसी भी टाइप के साथ const लगाया जा सकता है। इससे वे केवल पढ़ने योग्य हो जाते हैं।

प्रिमिटिव टाइप

C में कई प्रिमिटिव टाइप होते हैं और उनका आकार, बाइटों में, अलग-अलग हो सकता है। उनमें से कुछ को नीचे दी गई टेबल में उनके सामान्य आकार के साथ दिखाया गया है, जैसा x86-64 सिस्टम में होता है:

टाइप बाइटों की संख्या पूर्णांक/फ्लोटिंग-पॉइंट
_Bool 1 पूर्णांक
char 1 पूर्णांक
short 2 पूर्णांक
int 4 पूर्णांक
long 4 या 8 पूर्णांक
long long 8 पूर्णांक
float 4 फ्लोटिंग-पॉइंट
double 8 फ्लोटिंग-पॉइंट

इनमें से हर इंटीग्रल टाइप साइन्ड (डिफ़ॉल्ट) या अनसाइन्ड हो सकता है। अगर वे अनसाइन्ड हैं, तो यह लिखना ज़रूरी है, जैसे: unsigned int.

इंटीग्रल टाइप के लिए कुछ एलियस भी हैं, जो उनके आकार पर सटीक नियंत्रण देने के लिए बनाए गए हैं:

एलियस बाइटों की संख्या
int8_t 1
int16_t 2
int32_t 4
int64_t 8

इन एलियस के आगे u लगाकर अनसाइन्ड टाइप बताया जा सकता है, जैसे: uint64_t.

कुछ और ध्यान देने योग्य एलियस ये हैं:

एलियस टाइप
bool _Bool
size_t आमतौर पर एक अनसाइन्ड 8-बाइट पूर्णांक

_Bool, या उसका एलियस bool, इस मामले में अलग है कि हालाँकि यह पूरा 1 बाइट लेता है, फिर भी यह दो वैल्यू में से केवल एक ही ले सकता है: true (1) और false (0).

इन प्रिमिटिव टाइप की वैल्यू फंक्शन को पास की जाती हैं और फंक्शन से लौटाई जाती हैं, पूर्णांकों और फ्लोटिंग-पॉइंट वैल्यू के सामान्य नियमों के अनुसार।

एनम

एक एनम एक ऐसा टाइप है जो कई साफ़ तौर पर बताए गए एलिमेंट में से कोई एक ले सकता है:

enum example {
    example_1,
    example_2,
    example_3,
    example_4
};

C में एनम अपने आप एक पूर्णांक टाइप में बदल जाते हैं, जो आमतौर पर int होता है, यानी 4-बाइट का पूर्णांक।

डिफ़ॉल्ट रूप से एनम का पहला संभावित एलिमेंट 0 में बदल जाता है और उसके बाद के सारे एलिमेंट अगले पूर्णांक में बदल जाते हैं। तो, ऊपर बनाए गए enum example में example_1 की वैल्यू 0 होगी, example_2 की वैल्यू 1, और इसी तरह आगे भी।

एनम के किसी भी एलिमेंट की वैल्यू बताई जा सकती है, और फिर डिफ़ॉल्ट रूप से अगले एलिमेंट की वैल्यू उससे अगला पूर्णांक होगी:

enum example_2 {
    example_1 = 4,
    example_2 = 8,
    example_3
}; // example_3 is equal to 9

मेमोरी एड्रेस

C में किसी टाइप के मेमोरी एड्रेस को उस टाइप के पॉइंटर से दर्शाया जाता है और * ऑपरेटर से लिखा जाता है। तो, उदाहरण के लिए, int64_t * एक या अधिक int64_t के लिए मेमोरी की जगह को दर्शाता है।

एड्रेस के साथ आम तौर पर 8-बाइट के पूर्णांक जैसा व्यवहार किया जाता है।

ऐरे

ऐरे फंक्शन को उनके पहले एलिमेंट के पॉइंटर के रूप में पास किए जाते हैं और फंक्शन से भी उसी रूप में लौटाए जाते हैं।

int64_t example_arr[] = {1, 2, 3}; // this is an array of 3 signed 8-byte integers
fn(example_arr); // this is a function that passes a pointer (int64_t *) to the beginning of the array as an argument

ऐरे में हर एलिमेंट का आकार ऐरे के एलिमेंट टाइप के आकार जितना होता है। ऊपर बनाए गए example_arr में, उदाहरण के लिए, 8-8 बाइट के 3 एलिमेंट हैं, यानी कुल 24 बाइट।

चूँकि ऐरे अपनी लंबाई संग्रहीत नहीं करते, इसलिए एलिमेंट की संख्या बताने के लिए आमतौर पर एक अलग वैल्यू देनी पड़ती है, अगर किसी सेंटिनल वैल्यू से अंत न दर्शाया गया हो।

स्ट्रिंग

C में स्ट्रिंग char का ऐरे होती है, और char 1-बाइट का टाइप है। अधिकतर स्ट्रिंग ASCII अक्षरों की बनी होती हैं और NUL पर खत्म होती हैं, यानी वे वहाँ खत्म होती हैं जहाँ वैल्यू 0 वाला बाइट मिलता है। इसका मतलब है कि आमतौर पर स्ट्रिंग की लंबाई अलग आर्गुमेंट के रूप में पास करने की ज़रूरत नहीं पड़ती।

निर्देश

आपका दोस्त Chandler दुनिया भर के अनोखे देशों में घूमने की योजना बना रहा है। लेकिन दुख की बात है कि Chandler की गणित की समझ अच्छी नहीं है। उसे डर है कि यात्रा के दौरान करेंसी एक्सचेंज वाले उसे ठग न लें। इसलिए वह चाहता है कि आप उसके लिए एक करेंसी कैलकुलेटर बनाइए।

ऐप के लिए उसकी खास ज़रूरतें ये हैं।

Note

इस अभ्यास के फंक्शन एक C सोर्स फ़ाइल में दिए गए सिग्नेचर के साथ घोषित किए गए हैं। आपके x86-64-assembly कोड को उन्हीं खास बातों का पालन करना होगा।

1. अलग-अलग करेंसी का हिसाब रखिए

आपने Chandler की यात्राओं में इस्तेमाल होने वाली अलग-अलग करेंसी का हिसाब रखने के लिए एक enum currency_t बनाया है:

enum currency_t {
    GBP,
    EUR,
    JPY,
    AUD,
    BRL,
    CNY,
    CAD,
    INR
};

कभी-कभी आपको उन करेंसी को स्क्रीन पर प्रिंट करना पड़ता है, ताकि आप जाँच सकें कि वे सही हैं।

stringify_currency फंक्शन बनाइए:

void stringify_currency(char *buffer, enum currency_t currency);

इस फंक्शन का कोई रिटर्न वैल्यू नहीं है और इसे buffer से बताई गई जगह पर एक C-स्टाइल स्ट्रिंग रखनी चाहिए। स्ट्रिंग से यह दिखना चाहिए कि फंक्शन को किस तरह की करेंसी दी गई थी:

char buffer[4];
stringify_currency(buffer, CAD);
// => "CAD"

2. विनिमय दर जाँचिए

exchange_rate फंक्शन बनाइए:

double exchange_rate(enum currency_t domestic_currency, enum currency_t foreign_currency, const double *value_in_US_dollars);

domestic_currency आर्गुमेंट Chandler की उस स्थानीय करेंसी को दर्शाता है, जिसे foreign_currency के बदले बदला जाना है।

value_in_US_dollars double के एक ऐरे का पॉइंटर है, जिसमें हर करेंसी की एक इकाई का मूल्य अमेरिकी डॉलर में रखा है। इस ऐरे में करेंसी ही इंडेक्स होती हैं। इसलिए, अगर EUR enum currency_t का एक मान्य एलिमेंट है, तो value_in_US_dollars[EUR] से 1 EUR का मूल्य अमेरिकी डॉलर में मिलता है।

इस फंक्शन को विदेशी करेंसी की एक इकाई का मूल्य स्थानीय करेंसी में लौटाना चाहिए।

const double value_in_US_dollars[8] = {
    1.33329, 1.16443, 0.00654374, 0.650888,
    0.185577, 0.140420, 0.714142, 0.0113863
};

exchange_rate(JPY, GBP, value_in_US_dollars);
// => 203.750454633

3. नोटों का मूल्य निकालिए

get_value_of_bills फंक्शन बनाइए:

uint64_t get_value_of_bills(unsigned long long denomination, unsigned short number_of_bills);

denomination आर्गुमेंट एक अकेले नोट का मूल्य है, जबकि number_of_bills नोटों की कुल संख्या है।

यह करेंसी एक्सचेंज बूथ सिर्फ तय इकाइयों वाली नकदी का लेन-देन करता है। आपको मिलने वाली कुल रकम एक "नोट" या इकाई के मूल्य से विभाज्य होनी चाहिए, जिसमें कुछ अंश या शेष बच सकता है। आपके फंक्शन को सिर्फ उन नोटों का कुल मूल्य लौटाना चाहिए (अंश वाली रकम को छोड़कर) जो बूथ वापस देगा। दुख की बात है कि बची हुई रकम या खुल्ला बूथ अपने पास अतिरिक्त बोनस के तौर पर रख लेता है।

get_value_of_bills(5, 128);
// => 640

4. नोटों की संख्या निकालिए

get_number_of_bills फंक्शन बनाइए:

unsigned int get_number_of_bills(float amount, unsigned long long denomination);

इस फंक्शन को दी गई रकम के अंदर मिल सकने वाले करेंसी नोटों की संख्या लौटानी चाहिए। दूसरे शब्दों में: शुरुआती रकम में करेंसी के कितने पूरे नोट समा सकते हैं? याद रखिए, आपको सिर्फ पूरे नोट मिल सकते हैं, नोटों के अंश नहीं, इसलिए बँटवारा उसी हिसाब से कीजिए। यानी आप निकटतम पूरे नोट या इकाई तक नीचे की ओर गोल कर रहे हैं।

get_number_of_bills(127.5, 5);
// => 25

5. विनिमय के बाद मूल्य निकालिए

exchangeable_value फंक्शन बनाइए:

uint32_t exchangeable_value(float budget, double exchange_rate, uint8_t spread, unsigned long long denomination);

पैरामीटर spread विनिमय शुल्क के तौर पर लिया जाने वाला प्रतिशत है, जो पूर्णांक में लिखा जाता है। इसे 100 से भाग देकर दशमलव में बदलना होता है। ध्यान दीजिए कि spread विनिमय दर में जोड़ा जाता है, इसलिए 10 का spread विनिमय दर को 10% अधिक कर देता है।

इस फंक्शन को spread से समायोजित विनिमय दर निकालने के बाद नई करेंसी का अधिकतम मूल्य लौटाना चाहिए। याद रखिए कि करेंसी का denomination एक पूर्ण संख्या है और उसे छोटे हिस्सों में नहीं बाँटा जा सकता।

exchangeable_value(127.25, 0,873350884, 10, 20);
// => 120
exchangeable_value(127.25, 0,873350884, 10, 5);
// => 130
GitHub के ज़रिए संपादित करें यह लिंक एक नई विंडो या टैब में खुलता है
x86-64 Assembly Exercism

मुद्रा विनिमय शुरू करने के लिए तैयार हैं?

Exercism पर साइन अप कीजिए और x86-64 Assembly को 22 कॉन्सेप्ट130 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।