StringBuilder.Append和float一起使用

4

StringBuilder.Append 使用 float 时会截断值。它将转换为什么,如何阻止它截断?

AttributeOrderfloat 类型,构建字符串时我正在失去精度。

if ( AttributeOrder != 0 ) 
{ 
    if ( Result.Length > 0 ) 
    { 
        Result.Append( " AND " ); 
    } 
    Result.Append( COLUMN_ATTRIBUTE_ORDER ); 
    Result.Append( "=" ); 
    Result.Append( AttributeOrder ); 
} 

编辑:这是旧代码,我无法更改底层数据类型。SQL Server中的列是实数,数据类型为float。我需要将float按照其填充的方式呈现为字符串,以便于其他用途,并且不会失去精度。


如果你不能改变数据类型,那么你就无法解决这个问题。 - SLaks
我无法将 .NET 中的浮点数据类型转换为字符串吗? - rick schott
你不能将一个 float 转换成一个 24 位数的字符串。如果你愿意接受 9 位数,那么可以看一下我的回答的第一部分。 - SLaks
5个回答

5
你可以使用格式化字符串float转换为字符串,例如:
Result.Append(AttributeOrder.ToString("G9"))

或者,作为替代方案,

Result.AppendFormat("{0}={1:G9}", COLUMN_ATTRIBUTE_ORDER, AttributeOrder)

然而,使用 float 类型时,你将无法获得超过九位数字的精度,第八和第九位数字会出现不准确的情况。


float数据类型只能保留七位有效数字。一旦将值放入float变量中,就会永久性地丢失精度。为了存储SQL Server的全部24位精度,您需要使用decimal数据类型

一旦将变量更改为decimalAppend将给您与输入相同数量的数字。


是的。出于某种原因,我认为他已经意识到了这一点。 - SLaks
实数数据类型与float(24)相同——一个小数点右边有24位数字的浮点数据类型。因此,我可以安全地使用“G24”作为我的格式,并且不会失去精度吗? - rick schott
不行。一旦将值放入“float”中,您就会失去精度。您需要将数据放入“Decimal”而不是“float”中。 - SLaks
你能在回答中讨论垃圾回收(创建)吗?我想要一个不产生垃圾的解决方案。 - Xonatron
@MatthewDoucette:调用AppendFormat()不会直接分配内存。看起来实现是这样的;请参见源代码 - SLaks
显示剩余3条评论

2
如果您需要提供一个浮点数的表示,则应使用System.Decimal - System.Single无法准确表示您要显示的精度。
这个简单的例子展示了区别:
using System;   

class Test
{
    static void Main()
    {
        Single s = 1.23456789f;
        Decimal d = 1.23456789m;

        Console.WriteLine(s);
        Console.WriteLine(d);
    }
}

1
如果您使用Decimal类型,则无需担心指定精度 - 表示精度(即将数字显示为字符串)将完全与您设置的相同。 - Andrew Hare
@Andrew Hare - 十进制数将精确地显示存储的值,但这不一定完全等于您设置的值! - Jeffrey L Whitledge
1
@rick schott - 我认为格式化程序的精度不是需要关注的事情,而是变量的精度。如果你想要更高的精度,那么你应该切换到“double”。 - Jeffrey L Whitledge
@Andrew, Jeffrey:decimal 只能保留约28个有效数字。http://msdn.microsoft.com/en-us/library/364x0z75.aspx - SLaks
@rick:太晚了。一旦你将值放入float中,就完全无法恢复精度。 - SLaks
显示剩余4条评论

2
让我们来玩一下浮点数,并看看为什么它不起作用。
假设从 SQL 中读取了一个 24 位数字 1.23456789012345678901234,然后将其转换为 .Net 的 float 类型。
浮点数的二进制表示如下:0 01111111 00111100000011001010010。
第一个 0 是符号位,表示这个数是正数。01111111 是偏置指数,表示有效数字乘以 2^0(或 1)。00111100000011001010010 是数字的各位,减去第一位。
因此,浮点变量现在将数字 1.00111100000011001010010 编码为二进制数。
让我们看看当我们将该浮点数转换为十进制数时会发生什么。
1 * 1 =                         1
.
0 * 0.5 =                       0
0 * 0.25 =                      0
1 * 0.125 =                     0.125
1 * 0.0625 =                    0.0625
1 * 0.03125 =                   0.03125
1 * 0.015625 =                  0.015625
0 * 0.0078125 =                 0
0 * 0.00390625 =                0
0 * 0.001953125 =               0
0 * 0.0009765625 =              0
0 * 0.00048828125 =             0
0 * 0.000244140625 =            0
1 * 0.0001220703125 =           0.0001220703125
1 * 0.00006103515625 =          0.00006103515625
0 * 0.000030517578125 =         0
0 * 0.0000152587890625 =        0
1 * 0.00000762939453125 =       0.00000762939453125
0 * 0.000003814697265625 =      0
1 * 0.0000019073486328125 =     0.0000019073486328125
0 * 0.00000095367431640625 =    0
0 * 0.000000476837158203125 =   0
1 * 0.0000002384185791015625 =  0.0000002384185791015625
0 * 0.00000011920928955078125 = 0
                                ------------------------
                                1.2345678806304931640625

因此,如果我们显示浮点数的所有数字,我们得到1.2345678806304931640625。

那么这不是我们想要显示的数字吗?为什么会四舍五入这个数字?而且为什么这个值与我们开始的数字不同呢?

为了理解原因,让我们逐步查看一些相邻的浮点值:

binary floating point representation   decimal representation
------------------------------------   ----------------------
0 01111111 00111100000011001010000   = 1.2345676422119140625
0 01111111 00111100000011001010001   = 1.23456776142120361328125
0 01111111 00111100000011001010010   = 1.2345678806304931640625
0 01111111 00111100000011001010011   = 1.23456799983978271484375
0 01111111 00111100000011001010100   = 1.234568119049072265625

正如您所看到的,完全相同的浮点数用于表示此范围内的所有值:[1.234567821025848388671875,1.234567940235137939453125)

因此,在转换为浮点数时,第八位之后的任何小数位都会丢失,并且可能显示的任何数字都是完全无意义的,与实际表示的值无关。

第八位之后的所有小数位都是将24位二进制数四舍五入并转换为十进制数的结果,它们没有任何实际意义。


0
你可以在float上使用ToString()方法,像这样:
if ( AttributeOrder != 0 ) 
{ 
    if ( Result.Length > 0 ) 
    { 
        Result.Append( " AND " ); 
    } 
    Result.Append( COLUMN_ATTRIBUTE_ORDER ); 
    Result.Append( "=" ); 
    Result.Append( AttributeOrder.ToString("0.000000") ); 
} 

例如,上面的格式说明符显示了6位小数。如果您不想显示尾随零,请将0替换为#

0
你可以用 string.Format() 杀两只鸟。减少代码行数,以任意精度显示数字。
Result.Append(string.Format("{0}={1:0.000000}", COLUMN_ATTRIBUTE_ORDER, AttributeOrder)); 

1
你应该调用 AppendFormat。调用 Append(String.Format(...)) 会使 StringBuilder 失去意义。 - SLaks
你仍然无法显示比“float”可以包含的更高精度。 - SLaks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接