在 C++ 中没有外部库的情况下将 128 位从字符数组转换为十进制

converting 128 bits from a character array to decimal without external libraries in C++

我必须将大小为 16(每个字符 1 个字节)的字符数组的 128 位转换为十进制和十六进制,而不使用任何其他库。将其转换为十六进制很容易,因为每次处理四位,并且在生成后立即为每四位打印结果。

但是说到小数。不可能以正常的数学方式转换它,其中每个位乘以 2 的幂从左开始的位索引。

所以我想通过逐位打印来像处理十六进制那样转换它。但问题是,在十进制中,这是不可能的,因为最大数字是 9,它需要 4 位来表示,而 4 位可以表示最多 15 的十进制数。我尝试制作一些机制来携带额外的部分,但不能想办法做到这一点。我认为,这也行不通。我已经漫无目的地尝试了三天,因为我不知道该做什么。甚至无法在互联网上找到任何有用的解决方案。

所以,我想要一些方法来完成这项工作。

这是我的完整代码:

#include <iostream>
#include <cstring>
#include <cmath>

using namespace std;

const int strng = 128;
const int byts = 16;

class BariBitKari {

    char bits_ar[byts];

public:

    BariBitKari(char inp[strng]) {

        set_bits_ar(inp);
    }

    void set_bits_ar(char in_ar[strng]) {
        char b_ar[byts];

        cout << "Binary 1: ";
        for (int i=0, j=0; i<byts; i++) {

            for (int k=7; k>=0; k--) {
                if (in_ar[j] == '1') {
                    cout << '1';
                    b_ar[i] |= 1UL << k;
                }
                else if (in_ar[j] == '0') {
                    cout << '0';
                    b_ar[i] &= ~(1UL << k);
                }

                j++;
            }
        }
        cout << endl;

        strcpy(bits_ar, b_ar);
    }

    char * get_bits_ar() {
        return bits_ar;
    }


    // Functions

    void print_deci() {

        char b_ar[byts];

        strcpy(b_ar, get_bits_ar());

        int sum = 0;
        int carry = 0;

        cout << "Decimal : ";

        for (int i=byts-1; i >= 0; i--){

            for (int j=4; j>=0; j-=4) {

                char y = (b_ar[i] << j) >> 4;

                // sum = 0;

                for (int k=0; k <= 3; k++) {

                    if ((y >> k) & 1) {
                        sum += pow(2, k);
                    }
                }

                // sum += carry;
                // if (sum > 9) {
                //  carry = 1;
                //  sum -= 10;
                // }
                // else {
                //  carry = 0;
                // }
                // cout << sum;
            }
        }

        cout << endl;
    }

    void print_hexa() {

        char b_ar[byts];

        strcpy(b_ar, get_bits_ar());

        char hexed;

        int sum;

        cout << "Hexadecimal : 0x";

        for (int i=0; i < byts; i++){

            for (int j=0; j<=4; j+=4) {

                char y = (b_ar[i] << j) >> 4;

                sum = 0;

                for (int k=3; k >= 0; k--) {

                    if ((y >> k) & 1) {
                        sum += pow(2, k);
                    }
                }

                if (sum > 9) {
                    hexed = sum + 55;
                }
                else {
                    hexed = sum + 48;
                }
                cout << hexed;
            }
        }
        cout << endl;
    }
};

int main() {

    char ar[strng];

    for (int i=0; i<strng; i++) {
        if ((i+1) % 8 == 0) {
            ar[i] = '0';
        }
        else {
            ar[i] = '1';
        }
    }

    BariBitKari arr(ar);
    arr.print_hexa();
    arr.print_deci();

    return 0;
}

要将 128 位数字转换为“十进制”字符串,我将假设大十进制值只需要包含在一个字符串中,并且我们只是在“正数”中" space。在不使用适当的大数库的情况下,我将演示一种将任何字节数组转换为十进制字符串的方法。这不是最有效的方法,因为它会不断地解析、复制和扫描数字字符串。

我们将利用以下事实:

0x87654321 == 2,271,560,481

可以转换为以 8 位块移位的一系列字节。加回这些移位的块会得到原始值

0x87 << 24   == 0x87000000 == 2,264,924,160
0x65 << 16   == 0x00650000 ==     6,619,136
0x43 << 8    == 0x00004300 ==        17,152
0x21 << 0    == 0x00000021 ==            33

Sum          == 0x87654321 == 2,271,560,481

所以我们将 128 位数字转换为字符串的策略是:

  • 将原始的 16 字节数组转换为 16 个字符串 - 每个字符串代表数组每个字节的十进制等效值

  • 根据数组中原始字节的索引,将每个字符串“左移”适当的位数。利用左移相当于乘以2的事实

  • 将所有这些移位的字符串加在一起

因此,为了实现这一点,我们引入了一个函数,可以将两个字符串(仅由数字组成)“相加”:

// s1 and s2 are string consisting of digits chars only ('0'..'9')
// This function will compute the "sum" for s1 and s2 as a string
string SumStringValues(const string& s1, const string& s2)
{
    string result;
    string str1=s1, str2=s2;

    // make str2 the bigger string
    if (str1.size() > str2.size())
    {
        swap(str1, str2);
    }

    // pad zeros onto the the front of str1 so it's the same size as str2
    while (str1.size() < str2.size())
    {
        str1 = string("0") + str1;
    }

    // now do the addition operation as loop on these strings
    size_t len = str1.size();
    bool carry = false;
    while (len)
    {
        len--;

        int d1 = str1[len] - '0';
        int d2 = str2[len] - '0';

        int sum = d1 + d2 + (carry ? 1 : 0);
        carry = (sum > 9);
        if (carry)
        {
            sum -= 10;
        }

        result.push_back('0' + sum);
    }

    if (carry)
    {
        result.push_back('1');
    }

    std::reverse(result.begin(), result.end());
    return result;
}

接下来,我们需要一个函数来对十进制字符串进行“左移”:

// s is a string of digits only (interpreted as decimal number)
// This function will "shift left" the string by N bits
// Basically "multiplying by 2" N times
string ShiftLeftString(const string& s, size_t N)
{
    string result = s;

    while (N > 0)
    {
        result = SumStringValues(result, result); // multiply by 2
        N--;
    }
    return result;
}

然后把字节数组转成十进制字符串放在一起:

string MakeStringFromByteArray(unsigned char* data, size_t len)
{
    string result = "0";
    for (size_t i = 0; i < len; i++)
    {
        auto tmp = to_string((unsigned int)data[i]);   // byte to decimal string
        tmp = ShiftLeftString(tmp, (len - i - 1) * 8); // shift left
        result = SumStringValues(result, tmp);         // sum
    }
    return result;
}

现在让我们在上面使用的原始 32 位值上进行测试:

int main()
{
    // 0x87654321
    unsigned char data[4] = { 0x87,0x65,0x43,0x21 };
    cout << MakeStringFromByteArray(data, 4) << endl;
    return 0;
}

生成的程序将打印出来:2271560481 - 同上。

现在让我们在 16 字节的值上尝试一下:

int main()
{
    // 0x87654321aabbccddeeff432124681111
    unsigned char data[16] = { 0x87,0x65,0x43,0x21,0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x43,0x21,0x24,0x68,0x11,0x11 };
    std::cout << MakeStringFromByteArray(data, sizeof(data)) << endl;
    return 0;
}

以上打印:179971563002487956319748178665913454865

我们将使用 python 来仔细检查我们的结果:

Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:37:02) [MSC v.1924 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> int("0x87654321aabbccddeeff432124681111", 16)
179971563002487956319748178665913454865
>>>

我觉得不错。

我最初有一个实现可以在 32 位块而不是 8 位块中进行分块和求和。但是,涉及到小端与大端字节顺序问题。我将把潜在的优化作为改天的练习。