[英]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)
实现这一点。 一切都取决于您的号码的内部表示。
对于真正的二进制形式(2基数的功率,如256)
这是不可解决的O(n)
这个数字的十六进制打印在O(n)
然而你可以将十六进制字符串转换为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(Nnlog2(n))
我们可以用对数计算N
的n
,所以对于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))
对于真正的大数字而言,这是绝对的。
10基本二进制形式的权力
要在O(n)
执行此操作,您需要稍微更改数字的基数。 它仍将以二进制形式表示,但基数为10的幂。
例如,如果您的号码将由16bit WORDs
表示,您可以使用最高基数10000
,它仍然适合它(最大值为16536
)。 现在你可以轻松地以十进制打印,因此只需打印从MSW到LSW的数组中的每个单词。
例:
让大号1234567890
存储为BYTEs
,基数为100
, MSW排在第一位。 所以号码将按如下方式存储
BYTE x[] = { 12, 34, 56, 78, 90 }
但正如您在使用BYTEs
和100
基站时所看到的那样,我们浪费空间,因为在完整的BYTE
范围内仅使用100*100/256=~39%
。 这些数字的操作与原始二进制形式略有不同,因为我们需要处理上溢/下溢和携带标志的方式不同。
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)。 记下每个部门的提示 - 这些是您的十进制数字(第一个是最低有效数字)。
您发布的0x9a78563412
为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 */
因为这些操作比整数乘法和求和更简单,所以预期这样做会有一些(但不是很多)效率提高。 将其保留为第一个示例应该更具表现力,因为它更多地表示任意基本转换。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.