如何在C中将128位整数转换为十进制ascii字符串?

我正在尝试将存储为4个无符号整数的数组的128位无符号整数转换为C中的十进制字符串表示forms:

unsigned int src[] = { 0x12345678, 0x90abcdef, 0xfedcba90, 0x8765421 }; printf("%s", some_func(src)); // gives "53072739890371098123344" 

(上面的输入和输出示例完全是虚构的;我不知道输入会产生什么。)

如果我要使用hex,二进制或八进制,这将是一个简单的掩码和位移到剥离最不重要的字符。 但是,在我看来,我需要做基数为10的分裂。 不幸的是,我不记得如何跨多个int执行此操作,并且我使用的系统不支持大于32位的数据类型,因此使用128位类型是不可能的。 使用不同的语言也是不对的,我宁愿为这一项操作避开一个大数字库。

没有必要划分:

 #include  #include  typedef unsigned long uint32; /* N[0] - contains least significant bits, N[3] - most significant */ char* Bin128ToDec(const uint32 N[4]) { // log10(x) = log2(x) / log2(10) ~= log2(x) / 3.322 static char s[128 / 3 + 1 + 1]; uint32 n[4]; char* p = s; int i; memset(s, '0', sizeof(s) - 1); s[sizeof(s) - 1] = '\0'; memcpy(n, N, sizeof(n)); for (i = 0; i < 128; i++) { int j, carry; carry = (n[3] >= 0x80000000); // Shift n[] left, doubling it n[3] = ((n[3] << 1) & 0xFFFFFFFF) + (n[2] >= 0x80000000); n[2] = ((n[2] << 1) & 0xFFFFFFFF) + (n[1] >= 0x80000000); n[1] = ((n[1] << 1) & 0xFFFFFFFF) + (n[0] >= 0x80000000); n[0] = ((n[0] << 1) & 0xFFFFFFFF); // Add s[] to itself in decimal, doubling it for (j = sizeof(s) - 2; j >= 0; j--) { s[j] += s[j] - '0' + carry; carry = (s[j] > '9'); if (carry) { s[j] -= 10; } } } while ((p[0] == '0') && (p < &s[sizeof(s) - 2])) { p++; } return p; } int main(void) { static const uint32 testData[][4] = { { 0, 0, 0, 0 }, { 1048576, 0, 0, 0 }, { 0xFFFFFFFF, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0x12345678, 0x90abcdef, 0xfedcba90, 0x8765421 } }; printf("%s\n", Bin128ToDec(testData[0])); printf("%s\n", Bin128ToDec(testData[1])); printf("%s\n", Bin128ToDec(testData[2])); printf("%s\n", Bin128ToDec(testData[3])); printf("%s\n", Bin128ToDec(testData[4])); return 0; } 

输出:

 0 1048576 4294967295 4294967296 11248221411398543556294285637029484152 

直接除法基数2 ^ 32,以相反顺序打印十进制数字,使用64位算术,复杂度为O(n) ,其中n是表示中的小数位数:

 #include  unsigned int a [] = { 0x12345678, 0x12345678, 0x12345678, 0x12345678 }; /* 24197857161011715162171839636988778104 */ int main () { unsigned long long d, r; do { r = a [0]; d = r / 10; r = ((r - d * 10) << 32) + a [1]; a [0] = d; d = r / 10; r = ((r - d * 10) << 32) + a [2]; a [1] = d; d = r / 10; r = ((r - d * 10) << 32) + a [3]; a [2] = d; d = r / 10; r = r - d * 10; a [3] = d; printf ("%d\n", (unsigned int) r); } while (a[0] || a[1] || a[2] || a[3]); return 0; } 

编辑:更正了循环,如果数组a只包含零,则显示0。 此外,从左到右读取数组,[0]是最重要的,[3]是最低有效数字。

一种缓慢但简单的方法是使用减法将数字从最重要的数字打印到最不重要的数字。 基本上你需要一个函数来检查x >= y和另一个用于计算x -= y的情况。 然后你可以开始计算你可以减去10 ^ 38的次数(这将是最重要的数字),然后你可以减去10 ^ 37的次数…减去你可以减去1的次数。

以下是此方法的完整实现:

 #include  typedef unsigned ui128[4]; int ge128(ui128 a, ui128 b) { int i = 3; while (i >= 0 && a[i] == b[i]) --i; return i < 0 ? 1 : a[i] >= b[i]; } void sub128(ui128 a, ui128 b) { int i = 0; int borrow = 0; while (i < 4) { int next_borrow = (borrow && a[i] <= b[i]) || (!borrow && a[i] < b[i]); a[i] -= b[i] + borrow; borrow = next_borrow; i += 1; } } ui128 deci128[] = {{1u,0u,0u,0u}, {10u,0u,0u,0u}, {100u,0u,0u,0u}, {1000u,0u,0u,0u}, {10000u,0u,0u,0u}, {100000u,0u,0u,0u}, {1000000u,0u,0u,0u}, {10000000u,0u,0u,0u}, {100000000u,0u,0u,0u}, {1000000000u,0u,0u,0u}, {1410065408u,2u,0u,0u}, {1215752192u,23u,0u,0u}, {3567587328u,232u,0u,0u}, {1316134912u,2328u,0u,0u}, {276447232u,23283u,0u,0u}, {2764472320u,232830u,0u,0u}, {1874919424u,2328306u,0u,0u}, {1569325056u,23283064u,0u,0u}, {2808348672u,232830643u,0u,0u}, {2313682944u,2328306436u,0u,0u}, {1661992960u,1808227885u,5u,0u}, {3735027712u,902409669u,54u,0u}, {2990538752u,434162106u,542u,0u}, {4135583744u,46653770u,5421u,0u}, {2701131776u,466537709u,54210u,0u}, {1241513984u,370409800u,542101u,0u}, {3825205248u,3704098002u,5421010u,0u}, {3892314112u,2681241660u,54210108u,0u}, {268435456u,1042612833u,542101086u,0u}, {2684354560u,1836193738u,1126043566u,1u}, {1073741824u,1182068202u,2670501072u,12u}, {2147483648u,3230747430u,935206946u,126u}, {0u,2242703233u,762134875u,1262u}, {0u,952195850u,3326381459u,12621u}, {0u,932023908u,3199043520u,126217u}, {0u,730304488u,1925664130u,1262177u}, {0u,3008077584u,2076772117u,12621774u}, {0u,16004768u,3587851993u,126217744u}, {0u,160047680u,1518781562u,1262177448u}}; void print128(ui128 x) { int i = 38; int z = 0; while (i >= 0) { int c = 0; while (ge128(x, deci128[i])) { c++; sub128(x, deci128[i]); } if (i==0 || z || c > 0) { z = 1; putchar('0' + c); } --i; } } int main(int argc, const char *argv[]) { ui128 test = { 0x12345678, 0x90abcdef, 0xfedcba90, 0x8765421 }; print128(test); return 0; } 

十进制问题文本中的那个数字变为

 11248221411398543556294285637029484152 

和Python同意这是正确的值(这当然不代表代码是正确的!!! ;-))

同样的事情,但使用32位整数运算:

 #include  unsigned short a [] = { 0x0876, 0x5421, 0xfedc, 0xba90, 0x90ab, 0xcdef, 0x1234, 0x5678 }; int main () { unsigned int d, r; do { r = a [0]; d = r / 10; r = ((r - d * 10) << 16) + a [1]; a [0] = d; d = r / 10; r = ((r - d * 10) << 16) + a [2]; a [1] = d; d = r / 10; r = ((r - d * 10) << 16) + a [3]; a [2] = d; d = r / 10; r = ((r - d * 10) << 16) + a [4]; a [3] = d; d = r / 10; r = ((r - d * 10) << 16) + a [5]; a [4] = d; d = r / 10; r = ((r - d * 10) << 16) + a [6]; a [5] = d; d = r / 10; r = ((r - d * 10) << 16) + a [7]; a [6] = d; d = r / 10; r = r - d * 10; a [7] = d; printf ("%d\n", r); } while (a[0] || a[1] || a[2] || a[3] || a [4] || a [5] || a[6] || a[7]); return 0; } 

实际上你不需要实现长除法。 你需要用2的幂来实现乘法,并加法。 你有四个uint_32 。 首先将它们中的每一个转换为字符串。 将它们分别乘以(2^32)^3(2^32)^2(2^32)^1(2^32)^0 ,然后将它们加在一起。 您不需要进行基本转换,只需将四个部分组合在一起即可。 您显然需要确保字符串可以处理最大为UINT_32_MAX*(2^32)^3

假设您有一个快速的32位乘法和除法,通过实现bigint除法/模10000然后使用(s)printf输出数字组,可以一次计算4位数的结果。

这种方法也很容易扩展到更高(甚至可变)的精度……

 #include  typedef unsigned long bigint[4]; void print_bigint(bigint src) { unsigned long int x[8]; // expanded version (16 bit per element) int result[12]; // 4 digits per element int done = 0; // did we finish? int i = 0; // digit group counter /* expand to 16-bit per element */ x[0] = src[0] & 65535; x[1] = src[0] >> 16; x[2] = src[1] & 65535; x[3] = src[1] >> 16; x[4] = src[2] & 65535; x[5] = src[2] >> 16; x[6] = src[3] & 65535; x[7] = src[3] >> 16; while (!done) { done = 1; { unsigned long carry = 0; int j; for (j=7; j>=0; j--) { unsigned long d = (carry << 16) + x[j]; x[j] = d / 10000; carry = d - x[j] * 10000; if (x[j]) done = 0; } result[i++] = carry; } } printf ("%i", result[--i]); while (i > 0) { printf("%04i", result[--i]); } } int main(int argc, const char *argv[]) { bigint tests[] = { { 0, 0, 0, 0 }, { 0xFFFFFFFFUL, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0x12345678UL, 0x90abcdefUL, 0xfedcba90UL, 0x8765421UL } }; { int i; for (i=0; i<4; i++) { print_bigint(tests[i]); printf("\n"); } } return 0; } 

@Alexey Frunze的方法很简单,但速度很慢。 您应该使用上面的@ chill的32位整数方法。 另一种没有任何乘法或除法的简单方法是双重涉猎 。 这可能比chill的算法慢,但比Alexey的算法快得多。 跑完后你会得到一个十进制数的打包BCD