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

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

本文关键字:算法 十进制 十六进制数 转换      更新时间:2023-10-16

我有一个字节数组和该数组的长度。目标是输出包含表示为 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 字节),这肯定需要比我天真的算法更聪明的算法。

问题

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

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

您可以在O(n)中实现这一点,也无法实现这一点。一切都取决于您号码的内部表示形式。

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

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

    • 如何将 gi 范数整数(字符串格式)转换为十六进制格式?

    因为创建十六进制字符串不需要大数学。因此,您只需以十六进制将数组从MSW打印到LSW。这是O(n)但转换为DEC不是。

    要在十进制中打印bigint,您需要连续修改/除以10个从LSDMSD的数字,直到子结果为零。然后只需以相反的顺序打印它们...除法和模量可以一次完成,因为它们是相同的操作。因此,如果您的数字有N十进制数字,那么您需要N大除法。例如,每个 bigint 除法都可以通过二进制除法来完成,因此我们需要log2(n)位移位和减法,它们都是O(n)因此本机bigint打印的复杂性为:

    O(N.n.log2(n))
    

    我们可以从n通过对数计算N,因此BYTEs:

    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)。现在,您可以轻松地在十进制中打印,只需打印从MSWLSW的数组中的每个单词。

    例:

    让我们将大量1234567890存储为BYTEs,基本100MSW首先去的地方。所以数字将存储如下

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

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

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

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

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

当然,#2,#3都会使您的数字在O(n)中的十六进制打印变得不可能。

您发布的数字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 */

由于这些运算比整数乘法和求和更简单,因此预计这样做会有一些(但不大)的效率改进。 保留它应该更具表现力,因为它更多地表示任意基本转换。

你需要提高你的小学技能并实施长除法。

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