C# 在不分配内存的情况下将浮点转换为char[]

C# 在不分配内存的情况下将浮点转换为char[],c#,memory,allocation,C#,Memory,Allocation,您知道如何在不分配任何内存的情况下将浮点转换为字符缓冲区吗 =>我只想重新执行与float.ToString()相同的操作;因此,我可以将结果放入缓冲区,而不是分配字符串 我编写了一个函数,但它不能很好地处理“舍入”: 39.71变为“39.709996” 39.71001变为“39.710004” 这是因为39.71作为浮点是内存中存储值39.709996的四舍五入。通过对函数进行一些舍入,我可以很容易地得出如下结论: int capacity = 8192; // If you have

您知道如何在不分配任何内存的情况下将浮点转换为字符缓冲区吗

=>我只想重新执行与float.ToString()相同的操作;因此,我可以将结果放入缓冲区,而不是分配字符串

我编写了一个函数,但它不能很好地处理“舍入”:

  • 39.71变为“39.709996”
  • 39.71001变为“39.710004”
这是因为39.71作为浮点是内存中存储值39.709996的四舍五入。通过对函数进行一些舍入,我可以很容易地得出如下结论:

int capacity = 8192; // If you have some idea of the final string length.
var sb = new StringBuilder(capacity);

for (float x = 0; x < 1000; x += 1.2345f)
    sb.AppendFormat(", {0}", x);

char[] array = new char[sb.Length];

sb.CopyTo(0, array, 0, sb.Length); // Now array[] contains the result.
  • 39.71变为“39.71”
  • 39.71001变为“39.71”
这也不太好,因为我想保留与
float.ToString()
完全相同的算法,该算法能够编写“39.71”和“39.71001”

你知道这个
float.ToString()
是如何工作的吗


精确到我的目标:我想在一个非常大的字符串中附加大量的float(与其他类型混合),并在最后只分配这个字符串一次,以避免过多的垃圾收集。因此,我真的需要将浮点值转换成一个字符数组(不管是什么类型的格式,只是没有不可变的字符串)

从您的评论来看,您似乎希望能够将大量浮点值(和其他值类型)格式化成一个非常大的字符数组,而不必进行大量内存分配

无法避免所有内存分配,但您可以使用
StringBuilder.AppendFormat()
StringBuilder.CopyTo()
模拟它们

如果您知道最终字符数组的最大长度,可以初始化一个容量足够大的
StringBuilder
。这可能会减少内存分配的数量,但如果缓冲区太大,则会以浪费内存为代价

代码是这样的:

int capacity = 8192; // If you have some idea of the final string length.
var sb = new StringBuilder(capacity);

for (float x = 0; x < 1000; x += 1.2345f)
    sb.AppendFormat(", {0}", x);

char[] array = new char[sb.Length];

sb.CopyTo(0, array, 0, sb.Length); // Now array[] contains the result.
int-capacity=8192;//如果你对最后的字符串长度有一些概念。
var sb=新的StringBuilder(容量);
对于(浮动x=0;x<1000;x+=1.2345f)
sb.AppendFormat(“,{0}”,x);
char[]数组=新字符[sb.Length];
sb.CopyTo(0,数组,0,sb.Length);//现在,数组[]包含结果。
请注意,这里的最小缓冲区分配数为两个:一个用于
StringBuilder
使用的内部缓冲区,另一个用于char[]数组


然而,可能会有大量其他小型分配在幕后进行——但由于GC的工作方式,它们不太可能进入任何第1代集合,因此,它不太可能导致性能问题。

从您的评论来看,您似乎希望能够将大量浮点(和其他值类型)格式化为一个非常大的字符数组,而无需进行大量内存分配

无法避免所有内存分配,但您可以使用
StringBuilder.AppendFormat()
StringBuilder.CopyTo()
模拟它们

如果您知道最终字符数组的最大长度,可以初始化一个容量足够大的
StringBuilder
。这可能会减少内存分配的数量,但如果缓冲区太大,则会以浪费内存为代价

代码是这样的:

int capacity = 8192; // If you have some idea of the final string length.
var sb = new StringBuilder(capacity);

for (float x = 0; x < 1000; x += 1.2345f)
    sb.AppendFormat(", {0}", x);

char[] array = new char[sb.Length];

sb.CopyTo(0, array, 0, sb.Length); // Now array[] contains the result.
int-capacity=8192;//如果你对最后的字符串长度有一些概念。
var sb=新的StringBuilder(容量);
对于(浮动x=0;x<1000;x+=1.2345f)
sb.AppendFormat(“,{0}”,x);
char[]数组=新字符[sb.Length];
sb.CopyTo(0,数组,0,sb.Length);//现在,数组[]包含结果。
请注意,这里的最小缓冲区分配数为两个:一个用于
StringBuilder
使用的内部缓冲区,另一个用于char[]数组


然而,可能会有很多其他的小型分配在幕后进行——但是由于GC的工作方式,它们不太可能进入任何第1代集合,因此不太可能导致性能问题。

您可以调用C stdlib
sprintf()
和朋友,这将解决大多数格式和数据问题。有关示例,请参见。通过计算
sprintf()
的返回值来跟踪打印位置,即进入字符数组的索引,这一点很重要

跨越托管/非托管边界会带来一些不可避免的性能损失。一种可能的缓解策略是减少此类交叉的数量,例如,通过编写一个C包装函数,使用
s*printf()
,该函数接收大量浮点数组并一次性写入所有浮点


数据封送(即CLI和本机表示之间的来回转换)的潜在惩罚对于浮点和地址这样的POD来说可能不是问题,如果它们在两个世界中在位上完全相同。

您可以调用C stdlib
sprintf()
和friends,它们可以解决大多数格式和数据问题。有关示例,请参见。通过计算
sprintf()
的返回值来跟踪打印位置,即进入字符数组的索引,这一点很重要

跨越托管/非托管边界会带来一些不可避免的性能损失。一种可能的缓解策略是减少此类交叉的数量,例如,通过编写一个C包装函数,使用
s*printf()
,该函数接收大量浮点数组并一次性写入所有浮点


数据封送(即CLI和本机表示之间的来回转换)的潜在代价对于浮点和地址这样的POD来说可能不是问题,如果它们在两个世界中在位上完全相同。

下面是我最后编写的解决方案,这要感谢Marc Gravell提供的float.ToString()源代码

它简化了很多