Q नंबर फॉर्मेट बाइनरी फिक्स्ड-पॉइंट नंबर्स का वर्णन करने के लिए एक नोटेशन है।
फिक्स्ड-पॉइंट नंबर Solidity में फ्रैक्शनल (भिन्न) वैल्यूज़ को स्टोर करने के लिए एक लोकप्रिय डिज़ाइन पैटर्न है, क्योंकि यह भाषा फ्लोटिंग पॉइंट नंबर्स को सपोर्ट नहीं करती है। इस प्रकार, किसी नंबर के फ्रैक्शनल हिस्से को “कैप्चर” करने के लिए, हम फ्रैक्शन को एक होल नंबर (पूर्ण संख्या) से गुणा करते हैं ताकि फ्रैक्शनल हिस्से होल नंबर बन जाएं (जिसमें प्रिसिजन की कुछ संभावित हानि हो सकती है)।
Ether डेसिमल्स बनाम Q नंबर्स
Solidity प्रोग्रामिंग में सबसे प्रसिद्ध फिक्स्ड-पॉइंट नंबर “One Ether” है। एक Ether वास्तव में बेस यूनिट्स (Wei) होता है। “एक” Ether वास्तव में 1 गुणा है। चूंकि हम Solidity में “0.5” स्टोर नहीं कर सकते, इसलिए हम इसके बजाय या 500000000000000000 स्टोर करते हैं। अनिवार्य रूप से, को 0.5 से गुणा किया जाता है ताकि फ्रैक्शन एक इंटीजर के रूप में “संरक्षित” रहे। हालाँकि, यह कोई परफेक्ट समाधान नहीं है, क्योंकि यह से छोटी वैल्यूज़ को स्टोर नहीं कर सकता। फिर भी, अधिकांश एप्लिकेशन के लिए काफी है, और यदि अधिक प्रिसिजन की आवश्यकता हो तो कॉन्ट्रैक्ट्स एक बड़े नंबर का उपयोग कर सकते हैं।
दूसरी ओर, एक Q नंबर फ्रैक्शन को 10 की पावर के बजाय 2 की पावर से गुणा करता है क्योंकि EVM में 2 की पावर से गुणा (और भाग) करना अधिक गैस एफिशिएंट होता है, क्योंकि 2 की पावर से गुणा या भाग करना बिटशिफ्ट ऑपरेशन्स का उपयोग करके किया जा सकता है। उदाहरण के लिए, x << n, x * 2**n के समतुल्य है और x >> n, x / 2**n के समतुल्य है।
Q नंबर नोटेशन
एक Q नंबर को अक्सर Qm.n के रूप में लिखा जाता है, जहाँ n, 2 की वह पावर है जिसका उपयोग नंबर को गुणा करने के लिए किया जाता है और m इंटीजर हिस्से का अनसाइन्ड (unsigned) इंटीजर साइज़ है। यह कहना पूरी तरह से समान है कि m उन बिट्स की संख्या है जिनका उपयोग इंटीजर हिस्से को स्टोर करने के लिए किया जाता है और n उन बिट्स की संख्या है जिनका उपयोग फ्रैक्शन को स्टोर करने के लिए किया जाता है।
इस समानता को प्रदर्शित करने के लिए, Q1.1 नंबर पर विचार करें। यह नंबर इंटीजर हिस्से के लिए 1 बिट (m = 1) और फ्रैक्शन के लिए 1 बिट (n = 1) आवंटित करता है। नंबर 1 को के रूप में दर्शाया जाएगा जो 1 << 1 के समतुल्य है। या 1 << 1 का बाइनरी रिप्रजेंटेशन 10 है। ध्यान दें कि हमारा नंबर 2 बिट्स बड़ा है ()। हम 10₂ को इस प्रकार डिकंस्ट्रक्ट (विघटित) कर सकते हैं:
इस प्रकार, जो वेरिएबल “1” को Q1.1 के रूप में होल्ड करता है, वह वास्तव में वैल्यू 2, या बाइनरी 10₂ को स्टोर करेगा। हालाँकि, यदि वेरिएबल का उपयोग Q1.1 स्टोर करने के लिए किया गया है, तो हम 2 की वैल्यू की “व्याख्या” या उसे “मानकर” 1 के रूप में उपयोग करते हैं।
आइए Q8.4 नंबर के साथ एक उदाहरण देखें। नंबर 1 को या 1 << 4 के रूप में दर्शाया जाता है। 1 << 4 का बाइनरी रिप्रजेंटेशन 10000₂ है। चूँकि हमारे पास होल नंबर हिस्से को दर्शाने वाले 8 बिट्स हैं, हमें बाईं ओर (leftpad) तब तक शून्य लगाने की आवश्यकता है जब तक कि हमारा पूरा नंबर 12 बिट्स बड़ा न हो जाए:
नंबर को स्टोर करने के लिए कुल 12 बिट्स हैं, क्योंकि 8 + 4 = 12। “अंडर द हुड” (आंतरिक रूप से) हम वास्तव में वैल्यू स्टोर कर रहे हैं, लेकिन हम वेरिएबल वैल्यू की व्याख्या 1 के रूप में करते हैं। यह उसी तरह है जैसे हम किसी वेरिएबल को “1 Ether” होल्ड करते हुए मान सकते हैं, लेकिन “अंडर द हुड” वेरिएबल वास्तव में स्टोर कर रहा होता है। जब हम “अंडर द हुड” कहते हैं, तो हमारा मतलब उस वास्तविक नंबर से होता है जो मेमोरी या स्टोरेज में मौजूद है।
तीसरे उदाहरण के रूप में, एक Q4.8 नंबर पर विचार करें। नंबर 1 को या 1 << 8 के रूप में दर्शाया गया है। 1 << 8 का बाइनरी रिप्रजेंटेशन 10000000₂ है। चूँकि हमारे पास इंटीजर हिस्से को दर्शाने वाले 4 बिट्स हैं, हम बाईं ओर तीन शून्य लगाते हैं जब तक कि पूरा नंबर 12 बिट्स बड़ा न हो जाए:
Q4.8 और Q8.4 दोनों नंबर्स को स्टोर करने के लिए 12 बिट्स की आवश्यकता होती है। हालाँकि, Q8.4 नंबर में इंटीजर हिस्से के लिए अधिक बिट्स आवंटित होते हैं, और इसलिए यह एक बड़ा इंटीजर स्टोर कर सकता है। दूसरी ओर, Q4.8 नंबर में फ्रैक्शनल हिस्से के लिए अधिक बिट्स आवंटित होते हैं, इसलिए यह फ्रैक्शंस को दर्शाने में अधिक सटीक (precise) हो सकता है।
एक फिक्स्ड-पॉइंट नंबर के रूप में “1” की वैल्यू
आमतौर पर किसी Q नंबर के लिए, “एक” का मतलब 1 को से गुणा करना है जहाँ फ्रैक्शन को स्टोर करने के लिए बिट्स की संख्या है। इसलिए Q64.96 में 1 का मतलब या 1 << 96 है। Q128.128 में “एक” या 1 << 128 है। Q64.128 में “एक” भी 1 << 128 है क्योंकि हम केवल फ्रैक्शनल बिट्स की संख्या की परवाह करते हैं।
किसी भी Qm.n के लिए, इंटीजर 1 का रिप्रजेंटेशन हमेशा 1 << n होता है, और यह संयोग की बात है कि Q64.128 और Q128.128 दोनों में n = 128 है।
ध्यान दें कि n का अर्थ बाइनरी बिट्स (बेस 2) की संख्या है, न कि “डेसिमल्स की संख्या” जैसा कि दस की पावर (बेस 10) में होता है। Qx.18 वैसा नहीं है जैसे हम 1 ईथर को स्टोर करने के लिए का उपयोग करते हैं। Qx.18 का अर्थ है कि “एक” है न कि ।
Solidity में Q नंबर्स का उपयोग करना
एक Qm.n नंबर में बिट्स की संख्या m + n बिट्स बड़ी होनी चाहिए। इसलिए:
- एक Q64.64 नंबर को कम से कम एक uint128 का उपयोग करना चाहिए (128 = 64 + 64)
- एक Q64.96 नंबर को कम से कम एक uint160 का उपयोग करना चाहिए (160 = 64 + 96)
- एक Q128.128 नंबर को एक uint256 का उपयोग करना चाहिए
एक Q नंबर के फ्रैक्शनल हिस्से की व्याख्या करना
आइए एक Q1.1 नंबर की सभी संभावित वैल्यूज़ पर विचार करें:
| “अंडर द हुड” वैल्यू | फ्लोट वैल्यू (”अंडर द हुड” ÷ 2^1) | |
|---|---|---|
| 0 0 | 0 | 0 |
| 0 1 | 1 | 0.5 |
| 1 0 | 2 | 1.0 |
| 1 1 | 3 | 1.5 |
हम देख सकते हैं कि “फ्रैक्शन बिट” को 1 पर सेट करने का मतलब है कि फ्रैक्शन हिस्से में सिंगल बिट 0.5 वैल्यू को दर्शाता है। आम तौर पर, फ्रैक्शन बिट्स दो की फ्रैक्शनल पावर्स को दर्शाते हैं। उदाहरण के तौर पर आइए एक Q1.2 नंबर का उदाहरण देखें:
| “अंडर द हुड” वैल्यू | फ्लोट वैल्यू (”अंडर द हुड” ÷ 2^2 | |
|---|---|---|
| 0 00 | 0 | 0 |
| 0 01 | 1 | 0.25 (1/4) |
| 0 10 | 2 | 0.5 (2/4) |
| 0 11 | 3 | 0.75 (3/4) |
| 1 00 | 4 | 1.00 (4/4) |
| 1 01 | 5 | 1.25 (5/4) |
| 1 10 | 6 | 1.50 (6/4) |
| 1 11 | 7 | 1.75 (7/4) |
सामान्यतः, एक Q नंबर में बिट्स की व्याख्या इस प्रकार की जाती है। ध्यान दें कि डेसिमल (दशमलव) के दाईं ओर के बिट्स दो की फ्रैक्शनल पावर्स को दर्शाते हैं:
फ्रैक्शन में प्रत्येक बिट योगात्मक (additively) रूप से दो की फ्रैक्शनल पावर को दर्शाता है। उदाहरण के लिए, 0.1₂, 0.5 को दर्शाता है, 0.11₂, 0.75 को दर्शाता है, और 0.001₂, 0.125 या एक-आठवें (one eighth) को दर्शाता है।
Q नंबर्स केवल उन फ्रैक्शंस को एन्कोड कर सकते हैं जिन्हें 2 की पावर पर 1 के योग के रूप में दर्शाया जा सकता है। यदि हम 1/3 जैसे नंबर को दर्शाने का प्रयास करते हैं, तो निश्चित रूप से एक राउंडिंग एरर होगी।
नीचे दिए गए इंटरैक्टिव टूल में विभिन्न फ्रैक्शनल वैल्यूज़ डालकर देखें कि उन्हें फिक्स्ड-पॉइंट रिप्रजेंटेशन में कैसे बदला जाता है:
एक इंटीजर को Q नंबर में बदलना
इंटीजर 1 को Q64.96 फिक्स्ड-पॉइंट नंबर में बदलने के लिए, हम 1 << 96 की गणना करते हैं। यह एक बाइनरी नंबर बनाता है जिसमें 96वें स्थान पर 1 होता है और 0 से 95 तक (दोनों शामिल) बाइनरी वैल्यूज़ में शून्य होते हैं।
आम तौर पर, हम किसी इंटीजर को n बिट्स लेफ्ट-शिफ्ट करके Qm.n नंबर में बदल सकते हैं।
नीचे दिया गया एनीमेशन इसे दर्शाता है:
एक Q नंबर को इंटीजर में बदलना
एक Q नंबर में फ्रैक्शन हिस्सा होता है, लेकिन एक इंटीजर में नहीं होता है। इसलिए एक Q नंबर को इंटीजर में बदलने के लिए, हम बस इंटीजर हिस्से को बिटशिफ्ट कर देते हैं ताकि फ्रैक्शनल हिस्सा हट जाए।
इसलिए, यदि हम एक फिक्स्ड-पॉइंट नंबर का इंटीजर हिस्सा निकालना चाहते हैं, तो हम नंबर को n बिट्स से राइट-शिफ्ट करते हैं (याद रखें: n, Qm.n में फ्रैक्शनल बिट्स की संख्या है)। इससे फ्रैक्शनल बिट्स गायब हो जाते हैं, और हमारे पास केवल इंटीजर हिस्सा बचता है। दूसरे शब्दों में, हम एक फिक्स्ड-पॉइंट Q नंबर के सभी फ्रैक्शनल बिट्स को काटकर उसे एक इंटीजर में बदल रहे हैं।
एक Q4.4 नंबर को इंटीजर में बदलने के निम्नलिखित एनीमेशन पर विचार करें:
एक फिक्स्ड-पॉइंट वैल्यू का निर्माण करना
मान लीजिए कि हम “1.5” नंबर को Q64.96 के रूप में एन्कोड करना चाहते हैं। Solidity 1.5 * 2**96 या 1.5 << 96 को मान्य (valid) सिंटैक्स के रूप में स्वीकार नहीं करती है।
इसके बजाय, 1.5 की गणना इस प्रकार की जा सकती है:
1 * 2**96 + 2**96 / 2; // equivalent to 1 + 0.5
Q64.96 नंबर के रूप में 1.5 के लिए “अंडर द हुड” स्टोर की गई वैल्यू 118842243771396506390315925504 होगी। हम Python में इसकी गणना इस प्रकार कर सकते हैं:
>>> int(1.5 * 2**96)
118842243771396506390315925504
बाइनरी में, हम देख सकते हैं कि फ्रैक्शन के लिए 96 बिट्स का उपयोग किया जाता है:
>>> bin(118842243771396506390315925504)
'0b1 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' # space added for clarity
एक Q नंबर को फ्लोटिंग पॉइंट नंबर में बदलना (ऑफ़-चेन)
1.5 के हमारे पिछले उदाहरण का उपयोग करते हुए, हम 118842243771396506390315925504 को से भाग दे सकते हैं और प्राप्त कर सकते हैं:
जब हम ऑफ़-चेन किसी Q नंबर की फ्लोट वैल्यू की गणना करते हैं, तो हम 96 से बिटशिफ्ट करने के बजाय से भाग देते हैं। ऐसा इसलिए है क्योंकि बिटशिफ्टिंग सबसे दाईं ओर के 96 बिट्स को “नष्ट” कर देती है, जिससे फ्रैक्शन वाली जानकारी खो जाएगी।
सबसे बड़ी वैल्यू जो एक Q नंबर होल्ड कर सकता है
एक Q नंबर जो सबसे बड़ी इंटीजर वैल्यू होल्ड कर सकता है, वह है जहाँ इंटीजर हिस्से में बिट्स की संख्या है। जो सबसे बड़ी कुल वैल्यू यह होल्ड कर सकता है, वह सबसे बड़ा इंटीजर हिस्सा और सबसे बड़ा दर्शाया जा सकने वाला फ्रैक्शन है:
यह गणितीय रूप से के समतुल्य है। इसलिए, सबसे बड़ी कुल वैल्यू जो एक Qm.n नंबर स्टोर कर सकता है, वह है।
उदाहरण के लिए, एक Q96.64 की इंटीजर सबसे बड़ी वैल्यू या 79228162514264337593543950335 है। फ्रैक्शन वाले हिस्से को मिलाकर एक Q96.64 जो सबसे बड़ी वैल्यू स्टोर कर सकता है, वह है:
या
79228162514264337593543950335.9999999999999999999457898913757247782996273599565029144287109375
दो Q नंबर्स को एक साथ जोड़ना
यदि हम, उदाहरण के लिए, एक Q128.128 नंबर को Q64.64 नंबर में जोड़ना चाहते हैं, तो हमारे पास दो विकल्प हैं। पहला विकल्प यह है कि Q64.64 नंबर को 64 बिट्स से लेफ्ट बिटशिफ्ट किया जाए ताकि डेसिमल पॉइंट्स अलाइन (संरेखित) हो जाएँ। यह प्रभावी रूप से एक Q64.64 नंबर को Q64.128 नंबर में बदल देता है।
वैकल्पिक रूप से, हमारे पास दूसरा विकल्प भी है। यदि हमें 128 बिट्स के प्रिसिजन की आवश्यकता नहीं है, या हम विशेष रूप से योग (sum) के रूप में एक Q64.64 नंबर चाहते हैं, तो हम Q128.128 नंबर को 64 बिट्स से राइट बिटशिफ्ट कर सकते हैं, जो इसे Q128.64 नंबर में बदल देता है। बेशक, इसके परिणामस्वरूप प्रिसिजन की कुछ हानि होती है।
दो Q नंबर्स को एक साथ जोड़ने के लिए, हमें केवल यह आवश्यकता होती है कि उनके फ्रैक्शनल हिस्से में बिट्स की संख्या समान हो, अर्थात् डेसिमल पॉइंट्स अलाइन होने चाहिए। हालाँकि, “डेस्टिनेशन” डेटा टाइप में योग (sum) को संभालने के लिए पर्याप्त रूप से बड़ा होल-नंबर हिस्सा होना चाहिए, अन्यथा ओवरफ्लो (overflow) हो सकता है।
यदि हम दो Q नंबर्स को घटाना चाहते हैं, तो हम इसी अनुभाग में बताए गए समान तर्क का पालन करते हैं।
गुणा या भाग करना
यदि हम को गुणा करते हैं तो हम उत्तर के रूप में प्राप्त करने की अपेक्षा करते हैं। हालाँकि, अंडर द हुड, नंबर को के रूप में दर्शाया जाता है। इसलिए, यदि हम Q64.96 के रूप में दर्शाए गए नंबर 1 को स्वयं से गुणा करते हैं, तो हम वास्तव में का ऑपरेशन कर रहे होंगे, लेकिन हमें उत्तर के रूप में वास्तव में चाहिए।
इसलिए, जब हम दो Q नंबर्स को एक साथ गुणा करते हैं, तो हमें इसके बाद प्रोडक्ट (गुणनफल) को n बिट्स से राइट-शिफ्ट करना चाहिए। 96 बिट्स के हमारे उदाहरण में, इसका मतलब है कि को 96 बिट्स राइट-शिफ्ट करके बनाया जाएगा।
कुछ Solidity फिक्स्ड पॉइंट उदाहरण
उदाहरण 1: 5 को 2 से भाग दें
मान लीजिए कि हम 5 को 2 से भाग देना चाहते हैं और परिणाम को Q64.64 नंबर के रूप में रिटर्न करना चाहते हैं। चूँकि Q64.64 नंबर 64 बिट्स से बड़े इंटीजर्स को होल्ड नहीं कर सकता है, इसलिए हम uint64 का उपयोग करके इंटीजर्स 5 और 2 को दर्शा सकते हैं। वास्तव में एक Q64.64 को होल्ड करने के लिए 128 बिट्स की आवश्यकता होती है, इसलिए हम Q64.64 को होल्ड करने के लिए uint128 का उपयोग करेंगे।
दूसरे शब्दों में, इंटीजर्स को uint64 के साथ दर्शाया जाता है क्योंकि यह सबसे बड़ा इंटीजर है जिसे Q64.64 होल्ड कर सकता है, लेकिन Q64.64 नंबर को uint128 के साथ दर्शाया जाता है क्योंकि इसे इंटीजर के लिए 64 बिट्स और फ्रैक्शन के लिए 64 बिट्स को होल्ड करने की आवश्यकता होती है।
function divToQ64x64(uint64 x, uint64 y) public pure returns (uint128) {
// convert x (a uint64 integer)
// to a Q64.64 fixed-point number by left-shifting 64 bits.
uint128 x64_64 = x << 64;
// divide by y
return x64_64 / y;
}
divToQ64x64(5, 2); // returns 46116860184273879040
परिणाम 46116860184273879040 2.5 के रूप में एन्कोड होता है क्योंकि 46116860184273879040 / 2**64 = 2.5 है।
उदाहरण 2: 5 को 0.5 से गुणा करें
आइए 5 और 0.5 को एक साथ गुणा करें जब दोनों को Q64x64 के रूप में दर्शाया गया हो। इनके रिप्रजेंटेशन्स (निरूपण) इस प्रकार हैं:
- Q64.64 नंबर के रूप में 5,
5 << 64या5 * 2**64है जो 92233720368547758080 के बराबर है। - Q64.64 नंबर के रूप में 0.5,
1 << 64 / 2या2**64 / 2है जो 9223372036854775808 के बराबर है।
जब हम दो फिक्स्ड पॉइंट नंबर्स को एक साथ गुणा करते हैं, तो हमें यह सुनिश्चित करने की आवश्यकता होती है कि उन्हें फिर से नॉर्मलाइज़ करने के लिए से भाग देने से पहले वे ओवरफ्लो न हों। इसका मतलब है कि हम प्रोडक्ट को uint256 में स्टोर करेंगे और फिर 64 बिट्स से राइट-शिफ्ट करेंगे।
function mulU64x64(uint128 x, uint128 y) public pure returns (uint128) {
// note: Solidity performs multiplication using uint128 unless
// explicitly upcasted. This could overflow and revert.
uint256 temp = uint256(x) * uint256(y);
return uint128(temp >> 64);
}
mulU64x64(5 * 2**64, 2**64 / 2) // returns 46116860184273879040
यह अपेक्षित परिणाम लौटाता है क्योंकि 46116860184273879040 / 2**64 = 2.5 है।
उदाहरण 3: 5 को 0.5 से गुणा करें, लेकिन 5 फिक्स्ड पॉइंट के बजाय एक इंटीजर है
ऊपर दिए गए उदाहरण की भिन्नता के रूप में, हम 5 (एक इंटीजर) को 0.5 (एक फिक्स्ड पॉइंट नंबर) से गुणा करना चाहते हैं और एक फिक्स्ड पॉइंट नंबर रिटर्न करना चाहते हैं। ऊपर दिए गए कोड से एकमात्र अंतर 5 को इसके फिक्स्ड पॉइंट रिप्रजेंटेशन में बदलना है:
function mulUint64ByQ64x64(uint64 x, uint128 y) public pure returns (uint128) {
// convert uint64 to fixed point
uint128 x_fp = uint128(x) << 64;
uint256 temp = uint256(x_fp) * uint256(y);
return uint128(temp >> 64);
}
mulUint64ByQ64x64(5, 2**64 / 2) // returns 46116860184273879040
परिणाम अभी भी 2.5 का फिक्स्ड पॉइंट समतुल्य है।
पहले 64 से लेफ्ट-शिफ्ट करना और फिर बाद में राइट-शिफ्ट करना थोड़ा व्यर्थ है। ऊपर की गई समान गणना को इस प्रकार अधिक कुशलता से किया जा सकता है:
function mulUint64ByQ64x64(uint64 x, uint128 y) public pure returns (uint128) {
return uint128(uint256(x) * uint256(y));
}
mulUint64ByQ64x64(5, 2**64 / 2) // returns 46116860184273879040
Q नंबर्स का भाग करना
यदि हम 1 ÷ 1 की गणना करते हैं तो हम परिणाम 1 आने की अपेक्षा करते हैं। मान लीजिए कि हम Q64.64 का उपयोग कर रहे हैं। “1”, है। यदि हम की गणना करते हैं तो हमें परिणाम के रूप में 1 मिलता है, न कि । इसे सही करने के लिए, हम परिणाम को n बिट्स से लेफ्ट-शिफ्ट कर सकते हैं, लेकिन यह “प्रिसिजन के नुकसान से बचने के लिए भाग देने से पहले गुणा करें” (multiply before divide to avoid precision loss) सिद्धांत का उल्लंघन करता है। इसलिए, दो Q नंबर्स को विभाजित करने का सही तरीका यह है कि पहले न्यूमरेटर (अंश) को लेफ्ट-शिफ्ट किया जाए, और फिर भाग दिया जाए:
function divQ64x64ByQ64x64(uint128 x, uint128 y) public pure returns (uint128) {
return uint128(uint256(x) << 64 / uint256(y));
}
divQ64x64ByQ64x64(5, 2**64 / 2) // returns 46116860184273879040
सारांश
- Q नंबर्स इथेरियम में फ्रैक्शनल नंबर्स को होल्ड करने के लिए एक डिज़ाइन पैटर्न हैं।
- वे इथेरियम डेसिमल रिप्रजेंटेशन की तुलना में अधिक कुशल (efficient) हैं क्योंकि गुणा और भाग बिटशिफ्टिंग के साथ पूरा किया जा सकता है।
- एक Q नंबर को Qm.n के रूप में दर्शाया जाता है जहाँ
mइंटीजर के लिए बिट्स की संख्या है औरnफ्रैक्शन के लिए बिट्स की संख्या है। - डेसिमल के बाद प्रत्येक बिट 1/2, 1/4, 1/8, … आदि वैल्यू को दर्शाता है।
- एक इंटीजर को
nबिट्स के लिए लेफ्ट बिटशिफ्ट करके Q नंबर में बदला जा सकता है। फ्रैक्शन बिट्स को ट्रंकेट (काटकर) याnबिट्स राइट-शिफ्ट करके Q नंबर को इंटीजर में बदला जा सकता है। - यदि हम फ्लोटिंग पॉइंट्स को सपोर्ट करने वाली किसी भाषा में एक Q नंबर को से विभाजित करते हैं, तो हम इच्छित फ्रैक्शनल रिप्रजेंटेशन देख सकते हैं।
- दिए गए दो इंटीजर्स
aऔरbके लिए, सुनिश्चित करें किa,mबिट्स के भीतर फिट बैठता है।a << n / bके माध्यम से Qm.n नंबर के रूप में उनके अनुपात (ratio) की गणना करें। परिणामी फिक्स्ड-पॉइंट नंबर को होल्ड करने के लिए उपयोग किए जाने वाले बिट्स की संख्याaके बिट्स (m) औरnका योग होना चाहिए, यानी Qm.n। - Q नंबर्स को “उसी रूप में” (as is) एक साथ जोड़ा जा सकता है, जब तक कि डेसिमल्स अलाइन हों।
- यदि हम दो Q नंबर्स को एक साथ गुणा करते हैं, तो हमें परिणाम को
nसे राइट-शिफ्ट करने की आवश्यकता है ताकि परिणाम मेंnडेसिमल्स हों। - यदि हम दो Q नंबर्स को एक साथ विभाजित करते हैं, तो हमें पहले न्यूमरेटर को
nसे लेफ्ट-शिफ्ट करना होगा। - अस्थायी ओवरफ्लो से बचने के लिए भाग और गुणा दोनों में सावधानी बरतने की आवश्यकता है।