2009-04-20 11 views
10

मेरे पास एक प्रोजेक्ट है जहां एक फ़ंक्शन को चार 8-बिट वर्ण प्राप्त होते हैं और परिणामी 32-बिट आईईईई -754 फ्लोट को नियमित पर्ल नंबर में कनवर्ट करने की आवश्यकता होती है। ऐसा लगता है कि नीचे दिए गए कामकाजी कोड की तुलना में तेज़ तरीका होना चाहिए, लेकिन मैं काम करता है कि एक सरल पैक समारोह को समझने में सक्षम नहीं है।मैं चार पात्रों को पर्ल में 32-बिट आईईईई -754 फ्लोट में कैसे परिवर्तित कर सकता हूं?

यह काम नहीं करता है, लेकिन ऐसा लगता है जैसे कि यह करीब है:

$float = unpack("f", pack("C4", @array[0..3]); # Fails for small numbers 

काम करता है:

@bits0 = split('', unpack("B8", pack("C", shift))); 
@bits1 = split('', unpack("B8", pack("C", shift))); 
@bits2 = split('', unpack("B8", pack("C", shift))); 
@bits3 = split('', unpack("B8", pack("C", shift))); 
push @bits, @bits3, @bits2, @bits1, @bits0; 

$mantbit = shift(@bits); 
$mantsign = $mantbit ? -1 : 1; 
$exp = ord(pack("B8", join("",@bits[0..7]))); 
splice(@bits, 0, 8); 

# Convert fractional float to decimal 
for (my $i = 0; $i < 23; $i++) { 
    $f = $bits[$i] * 2 ** (-1 * ($i + 1)); 
    $mant += $f; 
} 
$float = $mantsign * (1 + $mant) * (2 ** ($exp - 127)); 

किसी को भी एक बेहतर तरीका है?

+1

मुझे खेद है कि आपका शीर्ष स्निपेट "काम नहीं करता है लेकिन करीब है" - क्या आप मतभेदों को इंगित कर सकते हैं? जैसे अनपैक() का परिणाम लेकर और इसे 4 बाइट्स में परिवर्तित कर, फिर इनपुट और अंतिम आउटपुट के बीच अलग-अलग बिट्स की तलाश कर रहे हैं? –

उत्तर

13

मैं विपरीत दृष्टिकोण लेगा: अनपॅकिंग भूल जाओ, थोड़ा twiddling के लिए छड़ी।

सबसे पहले, अपने 32 बिट शब्द को इकट्ठा करें। संकेत बिट, प्रतिपादक और अपूर्णांश:

my $sign = ($word & 0x80000000) ? -1 : 1; 
my $expo = (($word & 0x7F800000) >> 23) - 127; 
my $mant = ($word & 0x007FFFFF | 0x00800000); 

अपने नाव इकट्ठा:

my $word = ($byte0 << 24) + ($byte1 << 16) + ($byte2 << 8) + $byte3; 

अब शब्द के कुछ हिस्सों को निकालने:

endianness के आधार पर यह दूसरी तरह के आसपास होने के लिए हो सकता है
my $num = $sign * (2 ** $expo) * ($mant/(1 << 23)); 

Wikipedia पर कुछ उदाहरण हैं।

  • 0xC2ED4000 => -118.625 पर इसका परीक्षण किया और यह काम करता है।
  • ने इसे 0x3E200000 => 0.15625 पर परीक्षण किया और एक बग मिला! (निर्धारित)
  • infinities और Nans को संभालने के लिए जब $ एक्सपो == 255
+0

बहुत अच्छा। यह मेरे परीक्षण मामले के लिए काम करता है और दोगुना तेज़ है और उत्तर सही हैं (लगभग 100k अद्वितीय संख्याओं का परीक्षण किया जाता है)। भविष्य में मुझे अपने हाथों को गंदे करने और कुछ छोटे परिचालन करने की कोशिश करनी होगी। धन्यवाद! –

+0

कोई चिंता नहीं, आनंद लें। बहुत जल्दी होना चाहिए। पीएस: "जब $ expo == 255" पढ़ना चाहिए "$ expo == 128" ... मैं ऑफसेट भूल गया। – NickZoic

+0

मेरा $ शब्द = अनपैक ("एन", $ बाइट्स); बहुत तेज़ होना चाहिए –

5

यह करने के लिए सबसे अच्छा तरीका है pack() उपयोग करने के लिए है मत भूलना।

my @bytes = (0xC2, 0xED, 0x40, 0x00); 
my $float = unpack 'f', pack 'C4', @bytes; 

या स्रोत और गंतव्य अलग endianness अगर:

my $float = unpack 'f', pack 'C4', reverse @bytes; 

आप कहते हैं कि इस विधि "काम नहीं करता है - ऐसा लगता है जैसे कि यह करीब है" और "कम संख्या के लिए विफल रहता है", लेकिन आप एक उदाहरण नहीं देते हैं। मुझे लगता है कि आप जो वास्तव में देख रहे हैं वह गोल कर रहा है, उदाहरण के लिए, एक संख्या 1.234 के रूप में पैक की जाती है, लेकिन इसे 1.233 99 996757507 के रूप में अनपॅक किया जाता है। यह पैक() का एक कार्य नहीं है, लेकिन 4-बाइट फ्लोट की परिशुद्धता का है।

+1

आप ठीक से हो सकते हैं। दूसरी तरफ, मेरा कोड सटीक वही त्रुटियों का उत्पादन करेगा, मुझे लगता है। पैक/अनपैक 'एफ' रूपांतरण करता है "मूल प्रारूप में एक एकल-परिशुद्धता फ्लोट" से रूपांतरण करता है। ... शायद जो कुछ भी डैन चल रहा है वह * आईईईई -754 नहीं है? – NickZoic

+1

"मेरा कोड सटीक एक ही त्रुटि उत्पन्न करेगा"। आपका कोड * करता है * पैक() विधि के समान परिणाम उत्पन्न करता है लेकिन यह एक त्रुटि नहीं है, बस गोल है। उदाहरण के लिए 0x3F9DF3B6 अनपॅक करने का प्रयास करें जो 1.234 आपकी विधि और पैक विधि का उपयोग करके फ़्लोट के रूप में पैक किया गया है। साथ ही, इन दिनों पर चलने वाले सिस्टम के विशाल बहुमत के "देशी" फ्लोट प्रारूप आईईईई 754 हैं। यदि ओपी एक अलग फ्लोट प्रारूप के साथ सिस्टम पर था, तो यह असामान्य होगा कि वह इसके बारे में जान सके। – jmcnamara

+0

राउंडिंग ई -39 –

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