繁体   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