簡體   English   中英

在C / C ++中有效地轉換Hex,Binary和Decimal

[英]Efficiently convert between Hex, Binary, and Decimal in C/C++

我有正整數的3個基本表示:

  1. 十進制,無符號長變量(例如unsigned long int NumDec = 200 )。
  2. 十六進制,字符串變量(例如字符串NumHex =“C8”
  3. 二進制,字符串變量(例如字符串NumBin =“11001000”

我希望能夠以最有效的方式在所有3個表示中的數字之間進行轉換。 即實現以下6個功能:

unsigned long int Binary2Dec(const string & Bin) {}
unsigned long int Hex2Dec(const string & Hex) {}
string Dec2Hex(unsigned long int Dec) {}
string Binary2Hex(const string & Bin) {}
string Dec2Binary(unsigned long int Dec) {}
string Hex2Binary(const string & Hex) {}

每種方法最有效的方法是什么? 我可以使用C和C ++,但不能提升。

編輯:“效率”是指時間效率:最短的執行時間。

正如其他人指出的那樣,我將從sscanf()printf()和/或strtoul() 它們對於大多數應用程序來說足夠快,並且它們不太可能有bug。 但是,我會說,這些函數比你想象的更通用,因為它們必須處理非ASCII字符集,數字表示在任何基數等等。 對於某些域,可以擊敗庫函數。

因此,首先測量,如果這些轉換的性能確實是一個問題,那么:

1)在某些應用程序/域中,某些數字經常出現,例如零,100,200,19.95,可能是如此常見,以至於優化函數以使用一堆if()語句轉換這些數字是有意義的,然后回到通用庫函數。 2)如果最常見的100個數字使用表查找,然后回退到庫函數。 請記住,大型表可能不適合您的緩存,並且可能需要多個共享庫的間接性,因此請仔細測量這些內容以確保不降低性能。

您可能還想查看boost lexical_cast函數,但根據我的經驗,后者與較好的舊C函數相比較。

很多人都說過,它值得一遍又一遍地重復:在你有證據表明它們存在問題之前,不要優化這些轉換。 如果你做了優化,測量你的新實現,以確保它更快並確保你有自己的版本的大量單元測試,因為你將引入錯誤:-(

我建議只使用sprintfsscanf

另外,如果您對它的實現方式感興趣,可以查看glibc源代碼 ,即GNU C庫

為什么這些例程必須如此節省時間? 那種說法總是讓我懷疑。 你確定像strtol()這樣明顯的轉換方法太慢,或者你可以做得更好嗎? 系統功能通常非常有效。 它們有時較慢,無法支持通用性和錯誤檢查,但您需要考慮如何處理錯誤。 如果bin參數的字符不是'0'和'1',那么呢? 中止? 傳播大量錯誤?

你為什么用“Dec”代表內部代表? Dec,Hex和Bin應該用於表示字符串表示。 unsigned long整數沒有小數。 你在處理顯示十進制數字的字符串嗎? 如果沒有,那么你在這里會讓人感到困惑,並且會讓更多人感到困惑。

二進制和十六進制文本格式之間的轉換可以使用查找表快速有效地完成,但任何涉及十進制文本格式的內容都將更加復雜。

這取決於你所優化的是什么,“高效”是什么意思? 重要的是轉換速度快,占用內存少,程序員時間少,讀取代碼的其他程序員的WTF少,或者什么?

對於可讀性和易於實施的,你至少應該實現這兩個Dec2Hex()Dec2Binary()被調用剛剛strotul() 這使得它們成為單行,這對於該詞的至少一些上述解釋是非常有效的。

聽起來很像家庭作業問題,但是到底是什么......

簡短的回答是從long int轉換為字符串使用兩個查找表。 每個表應該有256個條目。 一個字節映射到十六進制字符串:0 - >“00”,1 - >“01”等。另一個將字節映射到位串:0 - >“00000000”,1 - >“00000001”。

然后對於long int中的每個字節,您只需要查找正確的字符串,然后將它們連接起來。

要將字符串轉換回long,您只需將十六進制字符串和位字符串轉換回十進制數,方法是將每個字符的數值乘以16或2的適當冪,然后將結果相加。

編輯:您也可以通過二進制搜索找到正確的字符串,使用相同的查找表進行向后轉換。 這將采用log(256)= 8比較你的字符串。 不幸的是,我沒有時間進行分析,比較字符串是否比乘法和添加整數要快得多。

讓我們暫時考慮一半的任務 - 從字符串化的基數n轉換為無符號長整數,其中n是2的冪(二進制的基數為2,十六進制的基數為16)。

如果你的輸入是理智的,那么這項工作只不過是比較,減法,轉移和一個或每個數字。 如果你的意見不是很明智,那就是它變得丑陋的地方,不是嗎? 做轉換超快並不難。 在任何情況下都做得好是挑戰。

所以我們假設您的輸入是理智的,那么轉換的核心就是:

unsigned long PowerOfTwoFromString(char *input, int shift)
{
    unsigned long val = 0;
    char upperLimit = 'a' + (1 << shift)
    while (*input) {
        char c = tolower(*input++);
        unsigned long digit = (c > 'a' && c < upperLimit) ? c - 'a' + 10 : c - '0';
        val = (val << shift) | digit;
    }
    return val;
 }

 #define UlongFromBinaryString(str) PowerOfTwoFromString(str, 1)
 #define UlongFromHexString(str) PowerOfTwoFromString(str, 4)

看看這有多容易? 並且它會在不合理的輸入上失敗。 你的大部分工作都是為了讓你的輸入更加清晰,而不是表現。

現在,這段代碼利用了兩次移位的功能。 它很容易擴展到基座4,基座8,基座32等。它不適用於兩個基座的非功率。 對於那些,你的數學必須改變。 你得到

val = (val * base) + digit

這組操作在概念上是相同的。 基數的乘法將等同於移位。 所以我很可能會使用完全通用的例程。 並在清理輸入的同時清理代碼。 那時候,strtoul可能是你最好的選擇。 這是strtoul 版本的鏈接。 幾乎所有的工作都是處理邊緣條件 - 這應該讓你知道你應該集中注意力的地方:正確,有彈性的代碼。 與節省的費用相比,使用位移的節省將是最小的,而不會因輸入錯誤而崩潰。

為什么不使用宏來將格式作為輸入。 如果你至少在C。

#define TO_STRING( string, format, data) \
sprintf( string, "##format##", data)
// Int
TO_STRING(buf,%d,i);
// Hex ( Two char representation )
TO_STRING(buf,%02x,i);
// Binary
TO_STRING(buf,%b,i);

或者您可以直接使用sprintf:或者您可以使用多個宏。

#define INT_STRING( buf, data) \
sprintf( buf, "%d", data)
#define HEX_STRING( buf, data) \
sprintf( buf, "%x", data)
#define BIN_TO_STRING( buf, data) \
sprintf( buf, "%b", data)

BIN_TO_STRING( loc_buf, my_bin );

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM