2014-09-10 8 views
5

एक रूबी Float मूल्य को देखते हुए, जैसे करने के लिए एक चल बिन्दु परिवर्तित,उसके संगत बिट खंडों

f = 12.125 

मैं फ्लोटिंग प्वाइंट नंबर के साइन युक्त एक 3-तत्व सरणी को समाप्त करना चाहते हैं (1 बिट), एक्सपोनेंट (11 बिट्स), और अंश (52 बिट्स)। (रूबी की फ्लोट आईईईई 754 डबल-प्रेसिजन 64-बिट प्रतिनिधित्व है।)

ऐसा करने का सबसे अच्छा तरीका क्या है? बिट-स्तरीय हेरफेर रूबी का मजबूत बिंदु प्रतीत नहीं होता है।

ध्यान दें कि मैं बिट्स चाहता हूं, न कि संख्यात्मक मूल्यों से वे मेल खाते हैं। उदाहरण के लिए, 1.0 के फ़्लोटिंग-पॉइंट मान के लिए [0, -127, 1] प्राप्त करने के बाद मैं नहीं हूं - मैं स्ट्रिंग फॉर्म या समकक्ष प्रतिनिधित्व में वास्तविक बिट्स चाहता हूं, जैसे ["0", "0ff", "000 0000 0000"]

+0

जॉन, [आईईईई 754] (http://en.wikipedia.org/wiki/IEEE_floating_point) के उल्लेख के लिए धन्यवाद, फ्लोट का प्रतिनिधित्व। मुझे इस बारे में पता नहीं था।अगर किसी पाठकों को अनुस्मारक की आवश्यकता होती है, तो एक उदाहरण गणना दी जाती है [यहां] (http://class.ece.iastate.edu/arun/cpre381/ieee754/ie4.html)। –

उत्तर

4

बिट डेटा को Arrays pack के माध्यम से उजागर किया जा सकता है क्योंकि फ्लोट आंतरिक रूप से कार्य प्रदान नहीं करता है।

str = [12.125].pack('D').bytes.reverse.map{|n| "%08b" %n }.join 
=> "0100000000101000010000000000000000000000000000000000000000000000" 

[ str[0], str[1..11], str[12..63] ] 
=> ["0", "10000000010", "1000010000000000000000000000000000000000000000000000"] 

यह स्ट्रिंग प्रस्तुति से इसे खींचने के लिए 'घरों के चारों ओर' थोड़ा सा है। मुझे यकीन है कि वहाँ मूल bytes से डेटा खींचने के लिए एक अधिक कुशल तरीका है हूँ ...


संपादित बिट स्तर हेरफेर मेरी रुचि बदलाव तो मैं एक प्रहार के आसपास था। रुबी में परिचालनों का उपयोग करने के लिए आपको एक इंटीजर होना चाहिए ताकि फ्लोट को 64 बिट int में परिवर्तित करने के लिए कुछ और unpack आईएनजी की आवश्यकता हो। बड़ा एंडियन/यानी 754 दस्तावेजी प्रतिनिधित्व काफी मामूली है। थोड़ा एंडियन प्रतिनिधित्व मैं इस बारे में इतना निश्चित नहीं हूँ। यह थोड़ा अजीब है, क्योंकि आप 11 बिट एक्सपोनेंट और 52 बिट मंटिसा के साथ पूर्ण बाइट सीमाओं पर नहीं हैं। बिट्स को खींचने और उन्हें छोटे एंडियन जैसा दिखने के लिए उन्हें स्वैप करना मुश्किल हो जाता है, और यह सुनिश्चित नहीं है कि यह सही है क्योंकि मैंने लेआउट का कोई संदर्भ नहीं देखा है। तो 64 बिट मान थोड़ा एंडियन है, मुझे यकीन नहीं है कि यह 64 बिट मूल्य के घटकों पर कैसे लागू होता है जब तक आप उन्हें कहीं और स्टोर नहीं करते हैं, जैसे कि मंटिसा के लिए 16 बिट इंट।

छोटे से छोटे से 11 बिट मान के लिए एक उदाहरण के रूप में, मैं जिस तरह की चीज कर रहा था वह सबसे महत्वपूर्ण बाइट बाएं 3 को सामने, या कम से कम महत्वपूर्ण 3 बिट्स को स्थानांतरित करना था।

v = 0x4F2 
((v & 0xFF) << 3) | (v >> 8)) 

यह वैसे भी है, उम्मीद है कि इसका कुछ उपयोग है।

class Float 
    Float::LITTLE_ENDIAN = [1.0].pack("E") == [1.0].pack("D") 

    # Returns a sign, exponent and mantissa as integers 
    def ieee745_binary64 
    # Build a big end int representation so we can use bit operations 
    tb = [self].pack('D').unpack('Q>').first 

    # Check what we are 
    if Float::LITTLE_ENDIAN 
     ieee745_binary64_little_endian tb 
    else 
     ieee745_binary64_big_endian tb 
    end 
    end 

    # Force a little end calc 
    def ieee745_binary64_little 
    ieee745_binary64_little_endian [self].pack('E').unpack('Q>').first 
    end 

    # Force a big end calc 
    def ieee745_binary64_big 
    ieee745_binary64_big_endian [self].pack('G').unpack('Q>').first 
    end 

    # Little 
    def ieee745_binary64_little_endian big_end_int 
    #puts "big #{big_end_int.to_s(2)}" 
    sign  = (big_end_int & 0x80 ) >> 7 

    exp_a = (big_end_int & 0x7F ) << 1 # get the last 7 bits, make it more significant 
    exp_b = (big_end_int & 0x8000) >> 15 # get the 9th bit, to fill the sign gap 
    exp_c = (big_end_int & 0x7000) >> 4 # get the 10-12th bit to stick on the front 
    exponent = exp_a | exp_b | exp_c 

    mant_a = (big_end_int & 0xFFFFFFFFFFFF0000) >> 12 # F000 was taken above 
    mant_b = (big_end_int & 0x0000000000000F00) >> 8 # F00 was left over 
    mantissa = mant_a | mant_b 

    [ sign, exponent, mantissa ] 
    end 

    # Big 
    def ieee745_binary64_big_endian big_end_int 
    sign  = (big_end_int & 0x8000000000000000) >> 63 
    exponent = (big_end_int & 0x7FF0000000000000) >> 52 
    mantissa = (big_end_int & 0x000FFFFFFFFFFFFF) >> 0 

    [ sign, exponent, mantissa ] 
    end 
end 

और परीक्षण ...

def printer val, vals 
    printf "%-15s sign|%01b|\n",   val,  vals[0] 
    printf " hex e|%3x|   m|%013x|\n", vals[1], vals[2] 
    printf " bin e|%011b| m|%052b|\n\n",  vals[1], vals[2] 
end 

floats = [ 12.125, -12.125, 1.0/3, -1.0/3, 1.0, -1.0, 1.131313131313, -1.131313131313 ] 

floats.each do |v| 
    printer v, v.ieee745_binary64 
    printer v, v.ieee745_binary64_big 
end 

मेरे मस्तिष्क टीआईएल बड़ा endian है! आप ध्यान देंगे कि इन चीजों के साथ काम किया जा रहा है दोनों बड़े एंडियन हैं। मैं दूसरी तरफ स्थानांतरित करने में असफल रहा।

+0

यह निश्चित रूप से निकटतम है अब तक उत्तर का। +1! –

+0

(ध्यान दें कि 'जी' हमेशा बड़े-एंडियन को चुनता है, इसलिए यह मेरे प्रश्न के अनुसार मूल रूप से मूल प्रारूप नहीं है। उदाहरण के लिए, x86 और x86_64 छोटे-अंत वाले हैं, और उन्हें उलटने की आवश्यकता होगी।) –

+0

'डी' और रिवर्स फिर? या सादा 'डी'? – Matt

3

मॉड्यूल से frexp का उपयोग करें। doc से:

fraction, exponent = Math.frexp(1234) #=> [0.6025390625, 11] 
fraction * 2**exponent     #=> 1234.0 

संकेत सा अपने आप ही पता लगाने के लिए आसान है।

+0

'frexp' द्वारा लौटाए गए मान आईईईई 754 सम्मेलन के अनुरूप नहीं हैं, हालांकि वे इसके बराबर हैं। यदि आप बिट्स में वास्तव में रूचि रखते हैं, तो आपको इसे ध्यान में रखना होगा। (शायद ऐतिहासिक) कारणों का अनुमान लगाया जाता है http://stackoverflow.com/questions/24928833/why-does-frexp-not-yield- वैज्ञानिक- नोटेशन –

+0

मुझे 'frexp' के बारे में पता है, लेकिन मैं वास्तविक चाहता हूं बिट्स, संख्यात्मक मानों से वे मेल नहीं खाते हैं - उदाहरण के लिए, एक्सपोनेंट के लिए मैं 0-1 और 1s की 11-वर्ण वाली स्ट्रिंग, या हेक्स अंकों की 3 वर्ण स्ट्रिंग आदि की अपेक्षा करता हूं। –

संबंधित मुद्दे