Skip to content

Update standard numeric format parsing to handle higher precision #22458

@pgovind

Description

@pgovind

Update the parsing/formatting logic to allow for precision more than 99 digits

Currently, the standard numeric format parsing logic is limited to precision <= 99. Some numeric types have more precision, but ToString(string format) does not expose it correctly. With dotnet/runtime#46874, we now support precision up to int.MaxValue. Precision > int.MaxValue will throw a FormatException. Since the change occurs in the parsing logic, this will affect all numeric types.

Version introduced

.NET 6 Preview 2

Old behavior

Did not support precision > 99. For ex: 32.ToString("C100") will print C132. This occurred because the parse routine interpreted precision more than 99 as a custom numeric format string. Consequently formats such as H100 would be interpreted as a custom format and print wrong values while H99 would throw because it was (correctly) interpreted as an unsupported standard format

New behavior

The new rule is: A format modifier with any number of digits is interpreted as a standard format + precision. If the value doesn't fit into an int, we throw.
For ex:
32.ToString("C100") will print $32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Unsupported formats such as 32.ToString("H100") will throw a FormatException.

Reason for change

Fix unexpected behavior when using higher precision.

Recommended action

If you are using formats such as G142 today, you are technically encountering a bug. If you want to maintain the previous behavior (i.e. where 42.ToString("G999") was intentionally meant to return G999), you can explicitly single quote the first character (i.e. 42.ToString("'G'999")). This will work on .NET Framework, .NET Core, and .NET 5+

Format strings, like the following, would continue to be interpreted as "custom numeric format strings":

  • start with any character that is not an ASCII alphabetical character (such as $ or è)
  • start with an ASCII alphabetical character but which are not followed by an ASCII digit (such as A$)
  • start with an ASCII alphabetical character, followed by an ASCII digit sequence, and then any character that is not an ASCII digit character (such as A55A).

Category

  • [ * ] Core .NET libraries

Affected APIs

BigInteger.ToString(string format)
BigInteger.ToString(string format, IFormatProvider provider)
BigInteger.TryFormat()
// Integral Types
int/uint/byte/sbyte/short/ushort/long/ulong.ToString(string format)
int/uint/byte/sbyte/short/ushort/long/ulong.ToString(string format, IFormatProvider provider)
int/uint/byte/sbyte/short/ushort/long/ulong.TryFormat()
// Floating Types
half/single/double/decimal.ToString(string format)
half/single/double/decimal.ToString(string format, IFormatProvider provider)
half/single/double/decimal.TryFormat()

Issue metadata

  • Issue type: breaking-change

Metadata

Metadata

Assignees

Labels

🏁 Release: .NET 6Issues and PRs for the .NET 6 releasebreaking-changeIndicates a .NET Core breaking change

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions