标准数字格式分析精度
在使用 ToString
和 TryFormat
将数字格式化为字符串时,.NET 现支持使用更高的精度值。
注意
在 .NET 7 中,再次更改了最大精度。 有关详细信息,请参阅数字格式字符串的最大精度。
更改描述
在将数字格式化为字符串,格式字符串中的精度说明符表示所生成的字符串中的位数。 根据格式说明符(即字符串开头处的字符),精度可表示总位数、有效位数或小数位数。
在之前的 .NET 版本中,标准数字格式分析逻辑限制为 99 或更低的精度。 某些数字类型具有更高的精度,但一些 ToString(string format)
不会正确公开它。 如果你指定高于 99 的精度(例如 32.ToString("C100")
),格式字符串将被解释为自定义数字格式字符串而不是“精度为 100 的货币”。 在自定义数字格式字符串中,字符将被解释为字符文本。 此外,根据精度值,包含无效格式说明符的格式字符串将以不同的方式解释。 H99
会对无效的格式说明符引发 FormatException,而 H100
被解释为自定义数字格式字符串。
从 .NET 6 开始,.NET 支持高达 Int32.MaxValue 的精度。 由带有任意位数的格式说明符组成的格式字符串会被解释为带有精度的标准数字格式字符串。 对于下述任一或两种情况,会引发 FormatException:
- 格式说明符字符不是标准格式说明符。
- 精度大于 Int32.MaxValue。
此更改是在影响所有数字类型的分析逻辑中实现的。
下表显示了各种格式字符串的行为更改。
格式字符串 | 旧行为 | .NET 6 及更高版本中的行为 |
---|---|---|
C2 |
表示带有两个小数位数的货币 | 表示带有两个小数位数的货币(无更改) |
C100 |
表示打印“C100”的自定义数字格式字符串 | 表示带有 100 个小数位数的货币 |
H99 |
由于存在无效的标准格式说明符“H”,引发 FormatException | 由于存在无效的标准格式说明符“H”,引发 FormatException(无更改) |
H100 |
表示自定义数字格式字符串 | 由于存在无效的标准格式说明符“H”,引发 FormatException |
引入的版本
.NET 6
更改原因
此更改会更正在对数字格式分析使用更高的精度时出现的意外行为。
建议的操作
在大多数情况下,无需执行任何操作,生成的字符串中会显示正确的精度。
不过,如果想要还原到之前的行为(即当精度高于 99 时,格式说明符被解释为文本字符),可将字符用单引号引起来或使用反斜杠对其进行转义。 例如,在之前的 .NET 版本中,42.ToString("G999")
会返回 G999
。 若要保持该行为,请将格式字符串更改为 "'G'999"
或 "\\G999"
。 这将适用于 .NET Framework、.NET Core 和 .NET 5(及更高版本)。
以下格式字符串将继续被解释为自定义数字格式字符串:
- 以任何非 ASCII 字母字符开头,例如
$
或è
。 - 以 ASCII 字母字符开头但后面不跟 ASCII 数字,例如
A$
。 - 以 ASCII 字母字符开头,后跟 ASCII 数字序列,然后再跟任何非 ASCII 数字字符,例如
A12A
。
受影响的 API
此更改是在影响所有数字类型的分析逻辑中实现的。
- System.Numerics.BigInteger.ToString(String)
- System.Numerics.BigInteger.ToString(String, IFormatProvider)
- System.Numerics.BigInteger.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
- System.Int32.ToString(String)
- System.Int32.ToString(String, IFormatProvider)
- System.Int32.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
- System.UInt32.ToString(String)
- System.UInt32.ToString(String, IFormatProvider)
- System.UInt32.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
- System.Byte.ToString(String)
- System.Byte.ToString(String, IFormatProvider)
- System.Byte.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
- System.SByte.ToString(String)
- System.SByte.ToString(String, IFormatProvider)
- System.SByte.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
- System.Int16.ToString(String)
- System.Int16.ToString(String, IFormatProvider)
- System.Int16.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
- System.UInt16.ToString(String)
- System.UInt16.ToString(String, IFormatProvider)
- System.UInt16.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
- System.Int64.ToString(String)
- System.Int64.ToString(String, IFormatProvider)
- System.Int64.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
- System.UInt64.ToString(String)
- System.UInt64.ToString(String, IFormatProvider)
- System.UInt64.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
- System.Half.ToString(String)
- System.Half.ToString(String, IFormatProvider)
- System.Half.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
- System.Single.ToString(String)
- System.Single.ToString(String, IFormatProvider)
- System.Single.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
- System.Double.ToString(String)
- System.Double.ToString(String, IFormatProvider)
- System.Double.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)
- System.Decimal.ToString(String)
- System.Decimal.ToString(String, IFormatProvider)
- System.Decimal.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)