将大十六进制数转换为十进制数的算法(基数为10)

时间:2017-07-23 06:04:40

标签: c++ arrays algorithm memory hex

我有一个字节数组和该数组的长度。目标是输出包含该数字的字符串,表示为基数为10的数字。

我的阵列是小端。这意味着第一个(arr[0])字节是最低有效字节。这是一个例子:

#include <iostream>
using namespace std;

typedef unsigned char Byte;

int main(){
  int len = 5;
  Byte *arr = new Byte[5];
  int i = 0;

  arr[i++] = 0x12;
  arr[i++] = 0x34;
  arr[i++] = 0x56;
  arr[i++] = 0x78;
  arr[i++] = 0x9A;

  cout << hexToDec(arr, len) << endl;
}

该数组由[0x12, 0x34, 0x56, 0x78, 0x9A]组成。我想要实现的函数hexToDec应返回663443878930,这是十进制数。

但是,问题是因为我的机器 32位所以它输出2018915346(注意这个数字来自整数溢出)。所以,问题是因为我使用天真的方式(迭代数组并将其乘以256到数组中位置的幂,然后乘以该位置的字节,最后加到总和)。这当然会产生整数溢出。

我也试过了long long int,但当然在某些时候会发生整数溢出。

我希望表示为十进制数的数组可以很长(超过1000个字节),definitelly需要比我天真的算法更聪明的算法。

问题

实现这一目标的好算法是什么?另外,我必须问的另一个问题是该算法的最佳复杂性是什么?可以用线性复杂度O(n)来完成,其中n是数组的长度吗?我真的想不出一个好主意。实施不是问题,我缺乏想法。

建议或想法如何做到这一点就足够了。但是,如果使用某些代码更容易解​​释,请随意用C ++编写。

3 个答案:

答案 0 :(得分:0)

您可以而且无法在O(n)中实现此目的。一切都取决于您的号码的内部表示。

  1. 对于真正的二进制形式(2个基数的功率,如256)

    这在O(n)中无法解决这个数字的十六进制打印在O(n)但是您可以将 HEX 字符串转换为decadic并返回如下:

    由于创建十六进制字符串不需要bignum数学。您只需将阵列从 MSW 打印到 HEX 中的 LSW 。这是O(n),但转换为 DEC 不是。

    要以十进制形式打印bigint,您需要连续对其进行mod / div 10,从 LSD MSD 获取数字,直到subresult为零。然后只需按相反的顺序打印它们......除法和模数可以一次完成,因为它们是相同的操作。因此,如果您的号码有N十进制数字,那么您需要N bigint分区。每个bigint分区都可以通过二进制除法完成,因此我们需要log2(n)位移和减法,这些都是O(n)所以原生bigint打印的复杂性是:

    O(N.n.log2(n))
    

    我们可以用N计算n的对数,以便BYTE s:

    N = log10(base^n)
      = log10(2^(8.n))
      = log2(2^(8.n))/log2(10)
      = 8.n/log2(10)
      = 8.n*0.30102999
      = 2.40824.n
    

    所以复杂性将是:

    O(2.40824.n.n.log2(n)) = O(n^2.log2(n))
    

    对于真正的大数字而言,这是绝对的。

  2. 10基本二进制形式的力量

    要在O(n)中执行此操作,您需要稍微更改数字的基数。它仍将以二进制形式表示,但基数为10的幂。

    例如,如果您的号码将由16bit WORDs表示,则您可以使用仍然适合其中的最高基数10000(最大值为16536)。现在您可以轻松地以十进制方式打印,因此只需打印阵列中的每个单词,从 MSW LSW

    示例:

    允许将大号1234567890存储为BYTEs,基数为100,其中 MSW 排在第一位。所以号码将按如下方式存储

    BYTE x[] = { 12, 34, 56, 78, 90 }
    

    但正如您在使用BYTEs和基座100时所看到的那样,我们正在浪费空间,因为在100*100/256=~39%范围内只使用了BYTE。这些数字的操作与原始二进制形式略有不同,因为我们需要以不同的方式处理上溢/下溢和携带标志。

  3. BCD(二进制编码的十进制)

    还有另一个选项是使用 BCD (二进制编码的十进制),它几乎与之前的选项相同,但基数10用于数字的单个数字...每个nibel( 4位)恰好包含一位数。处理器通常具有该数字表示的指令集。用法类似于二进制编码的数字,但在每次算术运算之后都是 BCD 恢复指令,称为DAA,它使用Carry和Auxiliary Carry标志状态来恢复 BCD 编码结果。要以十进制格式打印 BCD 中的值,您只需将值打印为 HEX 。我们之前的例子中的数字将用BCD编码,如下所示:

    BYTE x[] = { 0x12, 0x34, 0x56, 0x78, 0x90 }
    
  4. 当然,#2,#3 无法在O(n)打印您的号码 HEX

答案 1 :(得分:0)

你需要提高你的小学技能并实施long division

我认为你最好在基数16中实现长除法(每次迭代将数字除以0x0A)。记下每个部门的提示 - 这些是您的十进制数字(第一个是最低有效数字)。

答案 2 :(得分:0)

您发布的0x9a78563412号码,正如您以小端格式表示的那样,可以使用以下代码转换为正确的uint64_t

#include <iostream>
#include <stdint.h>

int main()
{
    uint64_t my_number = 0;
    const int base = 0x100; /* base 256 */
    uint8_t array[] = { 0x12, 0x34, 0x56, 0x78, 0x9a };

    /* go from right to left, as it is little endian */
    for (int i = sizeof array; i > 0;) {
        my_number *= base;
        my_number += array[--i];
    }
    std::cout << my_number << std::endl; /* conversion uses 10 base by default */
}

示例运行给出:

$ num
663443878930

因为我们在2的精确幂的基础上,我们可以使用

来优化代码
my_number <<= 8; /* left shift by 8 */
my_number |= array[--i]; /* bit or */

因为这些操作比整数乘法和求和更简单,所以预期这样做会有一些(但不是很多)效率提高。将它留在第一个例子中应该更具表现力,因为它更像是一个任意的基本转换。