C# 格式化浮点舍入/精度问题:N2 与 0.00

问题描述 投票:0回答:2

在 C# (.Net 7) 中:

float a = 13082987520.00f;
Console.WriteLine(a);
Console.WriteLine($"{a:N2}");
Console.WriteLine($"{a:0.00}");

我得到:

1.3082988E+10
13,082,987,520.00
13082990000.00

特别要注意最后两行之间的数字差异:

13082987520
vs
13082990000

当我尝试将此类数字写入 CSV 文件时,这会引起很多混乱。 在这种预期的行为中? 我们该如何解释呢?

注意这个问题与 ToString("N2") 和 ToString("0.00") 之间的差异非常不同。我知道格式方面的差异,但在这里我指出浮点数打印的一个潜在的意外实现细节(并且可能是一个错误?)。如上例所示,显然“N2”不仅仅是添加千位分隔符,而“0.00”则不会。

对于 double 则不存在同样的问题:

double b = 13082987520.00;
Console.WriteLine(b);
Console.WriteLine($"{b:N2}");
Console.WriteLine($"{b:0.00}");
13082987520
13,082,987,520.00
13082987520.00

这里的问题是:

  1. 对于(单个)浮点来说,哪一个是“正确的”? (其实就是印着“N2”的那个)
  2. 如何打印正确的内容(即使用“N2”显示的内容),而无需执行
    a.ToString("N2").Replace(",", string.Empty)
c# floating-point precision
2个回答
4
投票

来自 ECMA-334:2022 C# 语言标准,第 1 章8.3.7:

浮点类型

...

float
类型可以表示值......精度为7位数字。

(更多关于this问题中的浮点近似,简单的32位IEEE浮点数不能提供超过7位的精度)

因此这两个值都是正确的近似值,四舍五入到 7 位的值提供相等的四舍五入值:

13082987520

13082990000
      ^
      |
1234567   

浮点数适合近似值。对于精确值(例如,用于货币计算),您应该使用

decimal
代替。来自相同的标准通道。 8.3.8:

decimal
类型是适合金融和货币计算的128位数据类型。


0
投票

比较在 .NET Framework 4.8.1 和 .NET 7 下运行的代码的行为很有启发性:

                                       // .NET FX 4.8.1     | .NET 7
Console.WriteLine(a);                  // 1.308299E+10      | 1.3082988E+10
Console.WriteLine(a.ToString("N2"));   // 13,082,990,000.00 | 13,082,987,520.00
Console.WriteLine(a.ToString("0.00")); // 13082990000.00    | 13082990000.00

以下哪种格式的值是正确的?

可以说,它们都是“正确的”。他们同意

a
的值精确到小数点后七位,这是
float
数据类型承诺支持的全部内容。

为什么 .NET Framework 和 .NET 7 之间的格式值不同?

.NET Core 3.0 改进了浮点格式化代码,使其符合 IEEE 754-2008 标准

ToString()
现在返回最短的可往返字符串,而
ToString("N2")
返回精确值中的所有可用数字。

在 .NET 7 中,为什么像“0.00”这样的自定义格式字符串会对 7 位数字后的
float
值进行舍入?

显然,此行为是为了向后兼容

// SinglePrecisionCustomFormat and DoublePrecisionCustomFormat are used to ensure that
// custom format strings return the same string as in previous releases when the format
// would return x digits or less (where x is the value of the corresponding constant).
private const int SinglePrecisionCustomFormat = 7;

如何获得与“N2”相同但不带逗号的值?

尝试使用“F2”定点格式:

Console.WriteLine($"{a:F2}"); // 13082987520.00
© www.soinside.com 2019 - 2024. All rights reserved.