python實現float/double的0x轉化


1. 問題引出

最近遇到了一個小問題,即:

讀取文本文件的內容,然后將文件中出現的數字(包括double, int, float等)轉化為16進制0x存儲

原本以為非常簡單的內容,然后就着手去寫了python,但是寫着寫着發現不對:

python貌似沒辦法直接讀取內存數據;
因此不得不借助於C語言,這樣又引出了python如何調用C lib

開始寫c發現又有問題了:

int 類型的數據和float/double數據在內存中的存儲方式是不同的

因此花了一些力氣解決了這些問題,成功得將數字轉化為了16進制0x的存儲類型,特記錄一下,以備后續查詢,也可以讓有需要的童鞋有個參考。

2. 基本知識

完成本實驗前,你必須具備以下的基礎知識:

1). float/double在內存中的存儲方式

浮點數在內存中的存儲形式為二進制的科學計數法,即
mark
mark
其中,S為符號,P為階碼,F為尾數
其長度如下表所示:

總長度 符號 階碼 尾數
float 32 bit 1 8 23
double 64 bit 1 11 52

> **符號位 S:** 0代表正數,1代表負數 > **階碼位 P:** 為unsigned, 計算時候,需要將實際尾數減去`7F`, 即實際計算用的`P=eb-0x7F` > **尾數位 F:** 用二進制科學計算法表示后,去掉前面的恆定`1`,只保留小數點后的二進制數據

例1:32bit二進制 0x42 0xOA 0x1A 0xA0 轉化為十進制浮點數

符號位:S=0,0x42的最高位為0
階碼位:0x42<<1=0x84, 0x84-0x7F = 5
尾數位:0x0A1AA0為換算為十進制然后小數點前加1得到1.0789375

計算:1.0789375*2^5 = 34.526

例2:將十進制數50.265轉化為32位規格化的浮點數

N = 50.265
S = 0
N/2^P = 1.xxx, 因此,P=5
F=N/2^P=50.265/32=1.57078125
由以上可知:
符號位S=0
eb = P+0x7F=0x84
尾數d[2]d[1]d[0]= 0x490F5C

因此,最終結果為:0x42490F5C (記住eb需要移位哦)

2). python如何調用C lib

簡單起見,可參考該博客:http://blog.csdn.net/golden1314521/article/details/44055523
詳細內容可參考python官方文檔:https://docs.python.org/2/library/ctypes.html

3. 代碼

I. C 代碼:讀取float所在的內存地址

/*
 *Filename: ftoc.c
 */
 
#define uchar unsigned char
#define uint unsigned int

void ftoc(float fl, uchar arr[]) 
{
    void *pf;
    pf = &fl;

    uchar i;
    for(i=0; i<4; i++)
    {
        arr[i] = *((uchar *)pf+i);
    }

	return ;
}

II. 編譯此代碼為libftoc.so

gcc -shared -Wl,-soname,libftoc -o libftoc.so -fPIC ftoc.c
  • shared: 表示需要編譯為動態庫(.so)
  • Wl: 告訴編譯器將后面的參數傳遞給鏈接器
  • soname: 指定了動態庫的soname(簡單共享名,Short for shared object name)

更加詳細的介紹可參考:http://blog.csdn.net/zhoujiaxq/article/details/10213655

  • o : output,即要編譯成目標文件的名字
  • fPIC: 地址無關代碼(.so必須加此參數),詳情可自行搜索

III. python 代碼

#!/usr/bin/python

import os
import ctypes

lib_name = './libftoc.so'   #我自己的 c lib

filename = "rd.txt"


f1 = open(filename, 'r')
f2 = open('result.txt', 'w+')

#-----------------------------------
#check the number is float or not
def is_float(s):
    try:
        float(s)
        return True
    except ValueError:
        pass

#-----------------------------------

def ftoc(num):
    number = ctypes.c_float(num) #covert the python type to c type
    arr = ctypes.c_ubyte * 4
    parr = arr(ctypes.c_ubyte(), ctypes.c_ubyte(), ctypes.c_ubyte(), ctypes.c_ubyte())  #create a c-type(unsigned char)  array

    #access the c lib
    lib_ftoc = ctypes.CDLL(lib_name)

    #call the c lib function!!!
    #after this function, parr contains float's dec_number*4
    lib_ftoc.ftoc(ctypes.c_float(num), parr)
    
    lst=[]
    for i in range(4):
        lst.append(parr[i])
        lst[i] = hex(lst[i])[2:]    #get rid of '0x'
        if(len(lst[i]) < 2):
            lst[i] = '0'+lst[i]     #make data 8-bit
    string = lst[3]+lst[2]+lst[1]+lst[0]
    string = string.upper()         #uppercase the characters

    return string


#============================================
# main flow
#===========================================
lst = []
line = f1.readline()
while line:
    line.strip('\n')
    lst = line.split()

    for i in range(len(lst)):
        #if the number is digit
        if lst[i].isdigit():
            lst[i] = hex(int(lst[i]))
            lst[i] = lst[i][2:]         #get rid of '0x' 
            lst[i] = lst[i].upper()     
            if(len(lst[i]) < 8):
                lst[i] = '0'*(8-len(lst[i])) + lst[i]

        #if the number is float
        else: 
            if is_float(lst[i]):
                lst[i] = ftoc(float(lst[i]))

    for i in range(len(lst)):
        f2.write(lst[i])
        f2.write(' ')

    f2.write('\n')
    
    line = f1.readline()

f2.write('\n')

f1.close()
f2.close()

VI. 運行結果

運行前的文檔:
mark

運行后的結果輸出:
mark


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM