繁体   English   中英

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

[英]Algorithm for converting large hex numbers into decimal form (base 10 form)

我有一个字节数组和该数组的长度。 目标是输出包含表示为基数为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 ++编写。

你可以而且不能在O(n)实现这一点。 一切都取决于您的号码的内部表示。

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

    这是不可解决的O(n)这个数字的十六进制打印在O(n)然而你可以将十六进制字符串转换为decadic并返回如下:

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

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

     O(Nnlog2(n)) 

    我们可以用对数计算Nn ,所以对于BYTE

     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.nnlog2(n)) = O(n^2.log2(n)) 

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

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

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

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

    例:

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

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

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

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

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

     BYTE x[] = { 0x12, 0x34, 0x56, 0x78, 0x90 } 

当然, #2,#3都不可能在O(n)打印你的数字的HEX

你需要提高你的小学技能并实施长期分工

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

您发布的0x9a785634120x9a78563412 ,因为您以小端格式表示它,可以使用以下代码转换为正确的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 */

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM