在C ++中将十六进制转换为整数的最快方法是什么?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在C ++中将十六进制转换为整数的最快方法是什么?相关的知识,希望对你有一定的参考价值。

我试图尽快将十六进制char转换为整数。

这只是一行:int x = atoi(hex.c_str);

有更快的方法吗?

在这里,我尝试了一种更加动态的方法,而且速度稍快一些。

int hextoint(char number) {
    if (number == '0') {
        return 0;
    }
    if (number == '1') {
        return 1;
    }
    if (number == '2') {
        return 2;
    }
    /*
     *  3 through 8
     */
    if (number == '9') {
        return 9;
    }
    if (number == 'a') {
        return 10;
    }
    if (number == 'b') {
        return 11;
    }
    if (number == 'c') {
        return 12;
    }
    if (number == 'd') {
        return 13;
    }
    if (number == 'e') {
        return 14;
    }
    if (number == 'f') {
        return 15;
    }
    return -1;
}
答案

提议的解决方案比OP的if-else更快:

  • 无序地图查找表

如果您的输入字符串始终是十六进制数字,则可以将查找表定义为unordered_map

std::unordered_map<char, int> table {
{'0', 0}, {'1', 1}, {'2', 2},
{'3', 3}, {'4', 4}, {'5', 5},
{'6', 6}, {'7', 7}, {'8', 8},
{'9', 9}, {'a', 10}, {'A', 10},
{'b', 11}, {'B', 11}, {'c', 12},
{'C', 12}, {'d', 13}, {'D', 13},
{'e', 14}, {'E', 14}, {'f', 15},
{'F', 15}, {'x', 0}, {'X', 0}};

int hextoint(char number) {
  return table[(std::size_t)number];
}
  • 查找表为用户constexpr文字(C ++ 14)

或者如果你想要更快的东西而不是unordered_map,你可以使用具有用户文字类型的新C ++ 14工具,并在编译时将你的表定义为文字类型:

struct Table {
  long long tab[128];
  constexpr Table() : tab {} {
    tab['1'] = 1;
    tab['2'] = 2;
    tab['3'] = 3;
    tab['4'] = 4;
    tab['5'] = 5;
    tab['6'] = 6;
    tab['7'] = 7;
    tab['8'] = 8;
    tab['9'] = 9;
    tab['a'] = 10;
    tab['A'] = 10;
    tab['b'] = 11;
    tab['B'] = 11;
    tab['c'] = 12;
    tab['C'] = 12;
    tab['d'] = 13;
    tab['D'] = 13;
    tab['e'] = 14;
    tab['E'] = 14;
    tab['f'] = 15;
    tab['F'] = 15;
  }
  constexpr long long operator[](char const idx) const { return tab[(std::size_t) idx]; } 
} constexpr table;

constexpr int hextoint(char number) {
  return table[(std::size_t)number];
}

Live Demo

基准:

我使用最近在isocpp.org上发布的Nikos Athanasiou编写的代码运行基准测试,作为C ++微基准测试的提议方法。

比较的算法是:

1. OP的原始if-else

long long hextoint3(char number) {
  if(number == '0') return 0;
  if(number == '1') return 1;
  if(number == '2') return 2;
  if(number == '3') return 3;
  if(number == '4') return 4;
  if(number == '5') return 5;
  if(number == '6') return 6;
  if(number == '7') return 7;
  if(number == '8') return 8;
  if(number == '9') return 9;
  if(number == 'a' || number == 'A') return 10;
  if(number == 'b' || number == 'B') return 11;
  if(number == 'c' || number == 'C') return 12;
  if(number == 'd' || number == 'D') return 13;
  if(number == 'e' || number == 'E') return 14;
  if(number == 'f' || number == 'F') return 15;
  return 0;
}

2. Christophe提出的紧凑if-else:

long long hextoint(char number) {
  if (number >= '0' && number <= '9') return number - '0';
  else if (number >= 'a' && number <= 'f') return number - 'a' + 0x0a;
  else if (number >= 'A' && number <= 'F') return number - 'A' + 0X0a;
  else return 0;
}

3.修正了由g24l提出的处理大写字母输入的三元运算符版本:

long long hextoint(char in) {
  int const x = in;
  return (x <= 57)? x - 48 : (x <= 70)? (x - 65) + 0x0a : (x - 97) + 0x0a;
}

4.查找表(unordered_map):

long long hextoint(char number) {
  return table[(std::size_t)number];
}

其中table是前面显示的无序地图。

5.查找表(用户constexpr文字):

long long hextoint(char number) {
  return table[(std::size_t)number];
}

表是用户定义的文字,如上所示。

实验设置

我定义了一个将输入十六进制字符串转换为整数的函数:

long long hexstrtoint(std::string const &str, long long(*f)(char)) {
  long long ret = 0;
  for(int j(1), i(str.size() - 1); i >= 0; --i, j *= 16) {
    ret += (j * f(str[i]));
  }
  return ret;
}

我还定义了一个用随机十六进制字符串填充字符串向量的函数:

std::vector<std::string>
populate_vec(int const N) {
  random_device rd;
  mt19937 eng{ rd() };
  uniform_int_distribution<long long> distr(0, std::numeric_limits<long long>::max() - 1);
  std::vector<std::string> out(N);
  for(int i(0); i < N; ++i) {
    out[i] = int_to_hex(distr(eng));
  }
  return out;
}

我创建了分别填充50000,100000,150000,200000和250000随机十六进制字符串的向量。然后,对于每个算法,我运行100个实验并平均时间结果。

编译器是GCC 5.2版,带有优化选项-O3

结果:

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

讨论

从结果我们可以得出结论,对于这些实验设置,建议的表方法优于所有其他方法。 if-else方法到目前为止是最糟糕的,因为unordered_map虽然赢得了if-else方法,但它明显慢于其他提议的方法。

CODE

编辑:

stgatilov提出的方法的结果,按位运算:

long long hextoint(char x) {
    int b = uint8_t(x);
    int maskLetter = (('9' - b) >> 31);
    int maskSmall = (('Z' - b) >> 31);
    int offset = '0' + (maskLetter & int('A' - '0' - 10)) + (maskSmall & int('a' - 'A'));
    return b - offset;
}

enter image description here

编辑:

我还针对table方法测试了g24l的原始代码:

long long hextoint(char in) {
  long long const x = in;
  return x < 58? x - 48 : x - 87;
}

请注意,此方法不处理大写字母ABCDEF

结果:

enter image description here

表格方法仍然更快。

另一答案

对于不同的系统,这个问题显然可能有不同的答案,从这个意义上讲,它从一开始就是不适合的。例如,i486没有管道,奔腾没有SSE。

要问的正确问题是:“在X系统中将单个字符十六进制转换为dec的最快方法是什么,例如i686”。

在这里的方法中,对于具有多级流水线的系统,其答案实际上是相同或非常非常非常相同。任何没有管道的系统都会向查找表方法(LUT)弯曲,但如果内存访问速度慢,则条件方法(CEV)或按位评估方法(BEV)可能会受益于xor与负载的速度。给定CPU。

(CEV)将寄存器which is not prone to mis-prediction分解为2个加载有效地址进行比较和条件移动。所有这些命令在奔腾管道中都是可配对的。所以他们实际上进入了一个周期。

  8d 57 d0                lea    -0x30(%rdi),%edx
  83 ff 39                cmp    $0x39,%edi
  8d 47 a9                lea    -0x57(%rdi),%eax
  0f 4e c2                cmovle %edx,%eax

(LUT)分解为寄存器之间的mov和来自数据相关存储器位置的mov以及用于对齐的一些nops,并且应该采用最小的1个周期。如前所述,只有数据依赖。

  48 63 ff                movslq %edi,%rdi
  8b 04

以上是关于在C ++中将十六进制转换为整数的最快方法是什么?的主要内容,如果未能解决你的问题,请参考以下文章

在c ++中将4个字节转换为float的最快方法

在 C# 中将字典映射到数组的最快方法

在 C 中将字符串数组转换为 Int 数组的最佳方法

在 Python 3 中将字节转换为十六进制字符串的正确方法是啥?

C语言如何将64位整数转字符串

在c ++中将负整数转换为二进制字符串