如何将int转换为char 而不在C#中生成垃圾

毫无疑问,这似乎是一个奇怪的请求,考虑到ToString()Convert.ToString()的可用性,但我需要将无符号整数(即UInt32 )转换为其字符串表示,但我需要将答案存储到char[]

原因是我正在使用字符数组来提高效率,并且当目标char[]初始化为char[10]的成员(以保存UInt32.MaxValue的字符串表示)时,理论上应该是可能的在不生成任何垃圾的情况下进行转换(我的意思是不在托管堆中生成任何临时对象。)

谁能看到一个巧妙的方法来实现这一目标?

(我正在使用Framework 3.5SP1,以防任何方式相关。)

继上面的评论之后,我想知道log10是否太慢了,所以我写了一个不使用它的版本。

对于四位数字,此版本的速度提高约35%,十位数字的速度提高约16%。

一个缺点是它需要空间用于缓冲器中的全十位数。

我不发誓它没有任何错误!

 public static int ToCharArray2(uint value, char[] buffer, int bufferIndex) { const int maxLength = 10; if (value == 0) { buffer[bufferIndex] = '0'; return 1; } int startIndex = bufferIndex + maxLength - 1; int index = startIndex; do { buffer[index] = (char)('0' + value % 10); value /= 10; --index; } while (value != 0); int length = startIndex - index; if (bufferIndex != index + 1) { while (index != startIndex) { ++index; buffer[bufferIndex] = buffer[index]; ++bufferIndex; } } return length; } 

更新

我应该补充一点,我使用的是Pentium 4.更新的处理器可以更快地计算出超越函数。

结论

我昨天意识到我做了一个小学生错误并在调试版本上运行基准测试。 所以我再次运行它们但实际上并没有太大的区别。 第一列显示正在转换的数字中的位数。 其余列显示转换500,000个数字的时间(以毫秒为单位)。

结果:

  luc1 arx henk1 luc3 henk2 luc2 1 715 217 966 242 837 244 2 877 420 1056 541 996 447 3 1059 608 1169 835 1040 610 4 1184 795 1282 1116 1162 801 5 1403 969 1405 1396 1279 978 6 1572 1149 1519 1674 1399 1170 7 1740 1335 1648 1952 1518 1352 8 1922 1675 1868 2233 1750 1545 9 2087 1791 2005 2511 1893 1720 10 2263 2103 2139 2797 2012 1985 

ulong的结果:

  luc1 arx henk1 luc3 henk2 luc2 1 802 280 998 390 856 317 2 912 516 1102 729 954 574 3 1066 746 1243 1060 1056 818 4 1300 1141 1362 1425 1170 1210 5 1557 1363 1503 1742 1306 1436 6 1801 1603 1612 2233 1413 1672 7 2269 1814 1723 2526 1530 1861 8 2208 2142 1920 2886 1634 2149 9 2360 2376 2063 3211 1775 2339 10 2615 2622 2213 3639 2011 2697 11 3048 2996 2513 4199 2244 3011 12 3413 3607 2507 4853 2326 3666 13 3848 3988 2663 5618 2478 4005 14 4298 4525 2748 6302 2558 4637 15 4813 5008 2974 7005 2712 5065 16 5161 5654 3350 7986 2994 5864 17 5997 6155 3241 8329 2999 5968 18 6490 6280 3296 8847 3127 6372 19 6440 6720 3557 9514 3386 6788 20 7045 6616 3790 10135 3703 7268 

luc1:Lucero的第一个function

arx:我的function

henk1:Henk的function

luc3 Lucero的第三个function

henk2:Henk的函数没有复制到char数组; 即只是测试ToString()的性能。

luc2:Lucero的第二个function

奇特的顺序是他们创建的顺序。

我也没有henk1和henk2运行测试,所以没有垃圾收集。 其他三个function的时间几乎相同。 一旦基准测试超过三位数,内存使用就会稳定:因此GC在Henk的function期间发生,并且对其他function没有不利影响。

结论:只需调用ToString()

以下代码执行此操作,但有以下警告:它不尊重区域设置,但始终输出正常的十进制数字。

 public static int ToCharArray(uint value, char[] buffer, int bufferIndex) { if (value == 0) { buffer[bufferIndex] = '0'; return 1; } int len = (int)Math.Ceiling(Math.Log10(value)); for (int i = len-1; i>= 0; i--) { buffer[bufferIndex+i] = (char)('0'+(value%10)); value /= 10; } return len; } 

返回的值是char[]使用量。

编辑 (对于arx):以下版本避免浮点数学运算并就地交换缓冲区:

 public static int ToCharArray(uint value, char[] buffer, int bufferIndex) { if (value == 0) { buffer[bufferIndex] = '0'; return 1; } int bufferEndIndex = bufferIndex; while (value > 0) { buffer[bufferEndIndex++] = (char)('0'+(value%10)); value /= 10; } int len = bufferEndIndex-bufferIndex; while (--bufferEndIndex > bufferIndex) { char ch = buffer[bufferEndIndex]; buffer[bufferEndIndex] = buffer[bufferIndex]; buffer[bufferIndex++] = ch; } return len; } 

这里还有另一个变量,用于计算小循环中的位数:

 public static int ToCharArray(uint value, char[] buffer, int bufferIndex) { if (value == 0) { buffer[bufferIndex] = '0'; return 1; } int len = 1; for (uint rem = value/10; rem > 0; rem /= 10) { len++; } for (int i = len-1; i>= 0; i--) { buffer[bufferIndex+i] = (char)('0'+(value%10)); value /= 10; } return len; } 

我将基准测试留给想要做的人……;)

让我们保持简单并最大限度地利用现有代码:

 public static int ToCharArray(uint value, char[] buffer, int bufferIndex) { string txt = value.ToString(); txt.CopyTo(0, buffer, bufferIndex, txt.Length); return txt.Length; } 

由于txt是超级廉价的gen0垃圾,因此非常有效。

我来晚会的时间已经很晚了,但我想你可能无法获得比简单重新解释内存更快,更少内存要求的结果:

  [System.Security.SecuritySafeCritical] public static unsafe char[] GetChars(int value, char[] chars) { //TODO: if needed to use accross machines then // this should also use BitConverter.IsLittleEndian to detect little/big endian // and order bytes appropriately fixed (char* numPtr = chars) *(int*)numPtr = value; return chars; } [System.Security.SecuritySafeCritical] public static unsafe int ToInt32(char[] value) { //TODO: if needed to use accross machines then // this should also use BitConverter.IsLittleEndian to detect little/big endian // and order bytes appropriately fixed (char* numPtr = value) return *(int*)numPtr; } 

这只是一个想法的演示 – 你显然需要添加对char数组大小的检查,并确保你有正确的字节顺序编码。 您可以查看BitConverter反映辅助方法以进行这些检查。