जैसा कि आप जानते हैं, जावा में कोई अहस्ताक्षरित प्रकार नहीं हैं। यदि C में आप
unsigned int
(
char
,
long
) लिख सकते हैं, तो जावा में यह काम नहीं करेगा। हालांकि, अक्सर अहस्ताक्षरित संख्याओं के साथ अंकगणितीय संचालन करना आवश्यक है। पहली नज़र में, ऐसा लगता है कि अहस्ताक्षरित प्रकारों की वास्तव में आवश्यकता नहीं होती है (आधे से कम
MaxInt
संख्याओं के लिए
MaxInt
बारे में सोचें, यदि आपको अधिक संख्याओं की आवश्यकता है, तो मुझे बस
long
और आगे
BigInteger
लेना होगा)। लेकिन मुख्य अंतर वास्तव में नहीं है कि आप हस्ताक्षरित या अहस्ताक्षरित इंट में कितने अलग-अलग गैर-नकारात्मक नंबर डाल सकते हैं, लेकिन उन पर अंकगणितीय संचालन और तुलना कैसे की जाती है। यदि आप बाइनरी प्रोटोकॉल के साथ या बाइनरी अंकगणित के साथ काम करते हैं, जहां हर बिट का उपयोग महत्वपूर्ण है, तो आपको अहस्ताक्षरित मोड में सभी बुनियादी संचालन करने में सक्षम होना चाहिए। क्रम में इन कार्यों पर विचार करें:
बाइट को शॉर्ट में बदलें (int, long)
सामान्य कास्ट
(int) myByte
एक संकेत के साथ 32 बिट्स तक विस्तारित होगा - इसका मतलब है कि यदि बाइट का उच्च बाइट 1 पर सेट किया गया था, तो परिणाम एक ही नकारात्मक संख्या होगी, लेकिन 32-बिट प्रारूप में लिखा गया है:
0xff -> 0xffffffff (-1)
अक्सर यह वह नहीं है जो हम चाहेंगे। 32 अहस्ताक्षरित बिट्स का विस्तार करने और
0x000000ff
प्राप्त करने के लिए, जावा में आप लिख सकते हैं:
int myInt = myByte & 0xff; short myShort = myByte & 0xff;
साइन-कम तुलना
अहस्ताक्षरित तुलना के लिए, एक संक्षिप्त सूत्र है:
int compareUnsigned(int a, int b) { return Integer.compare( a ^ 0x80000000, b ^ 0x80000000 ); }
बाइट के लिए, क्रमशः छोटा और लंबा, स्थिरांक
0x8000
,
0x8000
और
0x8000000000000000L
।
जोड़, घटाव और गुणा
और यहां एक सुखद आश्चर्य है - ये ऑपरेशन किसी भी मामले में सही तरीके से किए जाते हैं। लेकिन अभिव्यक्तियों में, यह सावधानीपूर्वक सुनिश्चित करना आवश्यक है कि ऑपरेशन एक ही प्रकार की संख्याओं के साथ किए जाते हैं, क्योंकि किसी भी अंतर्निहित रूपांतरण को एक साइन एक्सटेंशन के साथ किया जाता है, और उम्मीद के अलावा अन्य परिणाम हो सकते हैं। इस तरह के कीड़े की जिद एक गलत स्क्रिप्ट को बहुत कम ही अंजाम दिया जा सकता है।
विभाजन
लाभांश -256 को 256 हम -1 देंगे। और हम
0x00ffffff
0xffffff00 / 0x100
0x00ffffff
देना
0x00ffffff
, न कि
0xffffffff (-1)
।
byte
,
short
और
int
समाधान उच्च बिट संख्या पर स्विच करना होगा:
int a = 0xffffff00; int b = 0x100; int c = (int) ((a & 0xffffffffL) / b);
लेकिन
long
साथ क्या करना है? ऐसे मामलों में
BigInteger
स्विच करना आमतौर पर एक विकल्प नहीं है - बहुत धीमा। यह केवल अपने हाथों में सब कुछ लेने और विभाजन को मैन्युअल रूप से लागू करने के लिए बनी हुई है। सौभाग्य से, हमारे सामने सबकुछ पहले से ही चोरी हो गया है -
Google अमरूद में
long
लिए अहस्ताक्षरित विभाजन का कार्यान्वयन है, और यह बहुत तेज़ है। यदि आप इस लाइब्रेरी का उपयोग नहीं कर रहे हैं, तो सबसे आसान तरीका यह है कि
अनसाइन्ट किए गए
लोंगों से सीधे कोड का एक टुकड़ा फाड़ दें।
जावा फाइल:
public static long divide(long dividend, long divisor) { if (divisor < 0) {
कोड को संकलित करने के लिए, आपको
compare(long, long)
के कार्यान्वयन को उधार लेना होगा:
public static int compare(long a, long b) { return Longs.compare(flip(a), flip(b)); }
और
Longs.compare(long, long)
+
flip(long)
:
private static long flip(long a) { return a ^ Long.MIN_VALUE; } public static int compare(long a, long b) { return (a < b) ? -1 : ((a > b) ? 1 : 0); }
बिट पाली
अंत में बिट ऑपरेशन के विषय को कवर करने के लिए, हम भी बदलावों को याद करते हैं। X86 असेंबलर में अलग-अलग टीमों का एक पूरा समूह होता है जो बिटवाइज़ शिफ्ट बनाते हैं - SHL, SHR, SAL, SAR, ROR, ROL, RCR, RCL। अंतिम 4 चक्रीय बदलाव करते हैं; जावा में कोई समकक्ष नहीं हैं। लेकिन तार्किक और अंकगणितीय बदलाव मौजूद हैं। लॉजिकल शिफ्ट (साइन को ध्यान में नहीं रखता है) - SHL (शिफ्ट लेफ्ट) और SHR (शिफ्ट राईट) - को क्रमशः
<<
और
>>>
ऑपरेटर्स द्वारा जावा में लागू किया जाता है। तार्किक पारियों का उपयोग करके, आप दो की शक्तियों द्वारा पूर्णांक गुणन और विभाजन को जल्दी से कर सकते हैं। अंकगणित शिफ्ट दाईं ओर SAR (साइन को ध्यान में रखता है) - SAR - ऑपरेटर द्वारा कार्यान्वित किया जाता है
>>
अंकगणित बाईं पारी तार्किक के बराबर है, और इसलिए इसके लिए कोई विशेष ऑपरेटर नहीं है। यह अजीब लग सकता है कि इस ऑपरेशन के लिए कोडांतरक के पास एक विशेष ओपकोड है, लेकिन वास्तव में यह वही करता है, अर्थात्, एसएएल पूरी तरह से एसएचएल व्यवहार को दोहराता है, और यह इंटेल से प्रलेखन द्वारा स्पष्ट रूप से संकेत दिया गया है:
शिफ्ट अंकगणित बाएं (SAL) और शिफ्ट लॉजिकल लेफ्ट (SHL) निर्देश एक ही ऑपरेशन करते हैं; वे गंतव्य ऑपरेंड में बिट्स को बाईं ओर स्थानांतरित करते हैं (अधिक महत्वपूर्ण बिट स्थानों की ओर)। प्रत्येक पारी की गिनती के लिए, गंतव्य ऑपरेंड का सबसे महत्वपूर्ण बिट सीएफ ध्वज में स्थानांतरित किया जाता है, और कम से कम महत्वपूर्ण बिट को मंजूरी दे दी जाती है (चित्र देखें Intel764 में 7-7 और IA-32 आर्किटेक्चर सॉफ्टवेयर डेवलपर का मैनुअल, वॉल्यूम 1 )।
यही है, SAL को केवल समरूपता के लिए जोड़ा गया था, इस तथ्य को ध्यान में रखते हुए कि दाईं ओर एक बदलाव के लिए तार्किक और अंकगणित में एक विभाजन है। खैर, गोसलिंग ने परेशान नहीं करने का फैसला किया (और, मुझे लगता है, सही काम किया)।
तो हमारे पास निम्नलिखित हैं:
a << 1;
अंतिम सिफारिशें
- जब अंकगणित संचालन करते हैं जो चयनित बिट ग्रिड में अतिप्रवाह कर सकते हैं, तो किसी को हमेशा सटीक रूप से यह दिखाना चाहिए कि चर मानने वाले स्वीकार्य मानों का क्या क्षेत्र हो सकता है और मुखरता से इन आक्रमणकारियों को ट्रैक कर सकते हैं। उदाहरण के लिए, यह स्पष्ट है कि जब दो मनमाना 32-बिट अहस्ताक्षरित संख्याओं को गुणा करते हैं, तो परिणाम 32 बिट्स में फिट नहीं हो सकता है, और यदि आपको अतिप्रवाह से बचने की आवश्यकता है, तो आपको यह सुनिश्चित करने की आवश्यकता है कि इस बिंदु पर ऐसी स्थिति कभी नहीं होगी जिसमें उत्पाद 32 बिट्स में फिट नहीं होता है। , या आपको पहले दोनों ऑपरेंड को लंबे (
a & 0xffffffffL
) परिवर्तित करना होगा। यहां, आप केवल एक ऑपरेंड को परिवर्तित करके आसानी से गलती कर सकते हैं। नहीं, आपको दोनों को लंबे समय तक बदलने की आवश्यकता है, क्योंकि यदि दूसरा ऑपरेंड नकारात्मक है, तो इसे साइन एक्सटेंशन के साथ लंबे समय तक बदल दिया जाएगा, और गुणा का परिणाम गलत होगा। - उन कोष्ठकों को सामान्य रूप से अभिव्यक्तियों में रखें जहाँ बिटवाइज़ ऑपरेशन का उपयोग किया जाता है। तथ्य यह है कि जावा में बिटवाइज़ ऑपरेटरों की प्राथमिकता कुछ अजीब है, और अक्सर एक स्पष्ट तरीके से व्यवहार करता है। कई घंटों के बाद सूक्ष्म बग देखने के लिए कुछ कोष्ठक जोड़ना बेहतर है।
- यदि आपको
long
स्थिर रहने की आवश्यकता है, तो स्थिर शाब्दिक के अंत में प्रत्यय L
को जोड़ना याद रखें। यदि ऐसा नहीं किया जाता है, तो यह long
नहीं होगा, लेकिन int
, और यदि आप संक्षेप में long
डाले जाते long
साइन एक्सटेंशन जो हमारे लिए अप्रिय है वह फिर से घटित होगा।