In most instances, we’re not dreaming up a brand-new format for our numeric strings; if we were, people probably wouldn’t understand what we meant. Consequently, the framework provides us with a whole bunch of standard numeric format strings, for everyday use. Let’s have a look at them in action.
Example 10-7 shows how we format a decimal as a currency value, using an overload of the standard ToString method.
decimal dollarAmount = 123165.4539M; string text = dollarAmount.ToString("C"); Console.WriteLine(text);
The capital C indicates that we want the decimal formatted as if it were a currency value; and here’s the output:
Notice how it has rounded to two decimal places (rounding down in this case), added a comma to group the digits, and inserted a dollar sign for us.
Note: Actually, I’ve lied to you a bit. On my machine the output looked like this:That’s because it is configured for UK English, not U.S. English, and my default currency symbol is the one for pounds sterling. We’ll talk about formatting and globalization a little later in this chapter.
That’s the simplest form of this “currency” format. We can also add a number after the C to indicate the number of decimal places we want to use, as Example 10-8 shows.
decimal dollarAmount = 123165.4539M; string text = dollarAmount.ToString("C3"); Console.WriteLine(text);
This will produce three decimal places in the output:
Notice that it is again rounding the result. If you want to truncate, or always round up, you’ll need to round the original value before you convert to a string.
This formatting style is available on all of the numeric types. (We’ll see some later that apply to only particular types.)
Decimal formatting is a bit confusingly named, as it actually applies to integer types, not the decimal type. It gets its name from the fact that it displays the number as a string of decimal digits (0–9), with a preceding minus sign (−) if necessary. Example 10-9 uses this format.
int amount = 1654539; string text = amount.ToString("D9");
We’re asking for nine digits in the output string, and it pads with leading zeros:
If you don’t supply a qualifying number of decimal digits, as Example 10-10 shows, it just uses as many as necessary.
int amount = -2895729; string text = amount.ToString("D");
Another one for integer types, hexadecimal formatting, shown in Example 10-11, represents numbers as a string of hex digits (0–9, A–F).
int amount = 256; string text = amount.ToString("X");
This produces the output:
As with the decimal format string, you can specify a number to indicate the total number of digits to which to pad the number, as shown in Example 10-12.
int amount = 256; string text = amount.ToString("X4");
This produces the output:
Notice that the method doesn’t prepend a 0x or similar; so there is nothing to distinguish this as a hex string, if you happen to hit a value that does not include the digits A–F. (The convention of preceding hexadecimal values with 0x is common in C family languages, which is why C# supports it for numeric constants, but it’s not universal. VB.NET uses the prefix &H, for example. All .NET languages share the same numeric types and formatting services, so if they printed hex numbers with a C# prefix, that would be annoying for users of other languages. If you want a prefix, you have to add it yourself.)
All numeric types can be expressed in exponential form. You will probably be familiar with this notation. For example, 1.05 × 103 represents the number 1050, and 1.05 × 10−3 represents the number 0.00105.
Developers use plain text editors, which don’t support formatting such as superscript, so there’s a convention for representing exponential numbers with plain, unformatted text. We can write those last two examples as 1.05E+003 and 1.05E-003, respectively. C# recognizes this convention for literal floating-point values. But we can also use it when printing out numbers.
To display this form, we use the format string E, with the numeric specifier determining how many decimal places of precision we use.
Note: It will always format the result with one digit to the left of the decimal point, so you could also think of the precision specified as “one less than the number of significant figures.”
Example 10-13 asks for exponential formatting with four digits of precision.
double amount = 254.23875839484; string text = amount.ToString("E4");
And here’s the string it produces:
If you don’t provide a precision specifier, as in Example 10-14, you get six digits to the right of the decimal point (or fewer, if the trailing digits would be zero).
Note: We’ll see later how these defaults can be controlled by the framework’s globalization features
double amount = 254.23875839484; string text = amount.ToString("E");
Another format string that applies to all numeric types, the fixed-point format provides the ability to display a number with a specific number of digits after the decimal point. As usual, it rounds the result, rather than truncating. Example 10-15 asks for four digits after the decimal point.
double amount = 152.68385485; string text = amount.ToString("F4");
The output will be padded with trailing zeros if necessary. Example 10-16 causes this by asking for four digits where only two are required.
double amount = 152.68; string text = amount.ToString("F4");
So, the output in this case is:
Sometimes you want to use fixed point, if possible, but if an occasional result demands a huge number of leading zeros, you’d prefer to fall back on the exponential form (rather than display it as zero, for instance). The “general” format string, illustrated in Example 10-17, will provide you with this behavior. It is available on all numeric types.
double amount = 152.68; string text = amount.ToString("G4"); Console.WriteLine(text); double amount2 = 0.00000000000015268; text = amount2.ToString("G4"); Console.WriteLine(text);
This will produce the following output:
Note that the precision string determines the number of significant figures in either case, not the number of decimal places (as per the fixed-point and exponential forms). As usual, rounding is used if there are more digits than the precision allows. And if you do not specify the precision (i.e., you just use "G") it chooses the number of digits based on the precision of the data you’re using—float will show fewer digits than double, for example.
Note: If you don’t specify a particular format string, the default is as though you had specified "G".
The numeric format, shown in Example 10-18, is very similar to the fixed-point format, but adds a “group” separator for values with enough digits (just as the currency format does). The precision specifier can be used to determine the number of decimal places, and rounding is applied if necessary.
double amount = 1520494.684848; string text = amount.ToString("N4"); Console.WriteLine(text);
This will produce the following output:
Very often you need to display a number as a percentage. However, it’s common to maintain values which represent a percentage using one of the floating-point types, predivided by 100 for ease of future manipulation.
The more mathematically minded among you probably rail against people calling the value 0.58 “a percentage” when they really mean 58%; but it is, unfortunately, a somewhat common convention in computer circles. Worse, it’s not consistently applied, making it hard to know whether you are dealing with predivided values, or “true” percentages. It can get especially confusing when you are frequently dealing with values less than 1 percent:
double interestRatePercent = 0.2;
Is that supposed to be 0.2 percent (like I get on my savings) or 20 percent APR (like my credit card)? One way to avoid ambiguity is to avoid mentioning “percent” in your variable names and always to store values as fractions, representing 100 percent as 1.0, converting into a percentage only when you come to display the number.
The percent format is useful if you follow this convention: it will multiply by 100, enabling you to work with ratios internally, but to display them as percentages where necessary. It displays numbers in a fixed-point format, and adds a percentage symbol for you. The precision determines the number of decimal places to use, with the usual rounding method applied. Example 10-19 asks for four decimal places.
double amount = 0.684848; string text = amount.ToString("P4"); Console.WriteLine(text);
This will produce:
Note that this format works with any numeric type—including the integer types. There’s no special treatment for an Int32 or Int16, for example. They are multiplied up by 100, in just the same way as the floating-point types. This means that you can’t format values in increments of less than 100 percent with an integer. For instance, 0 × 100 implies 0 percent, 1 × 100 implies 100 percent, and so on.
The last of the standard numeric format strings we’re going to look at is the round-trip format. This is used when you are expecting the string value to be converted back into its numeric representation at some point in the future, and you want to guarantee no loss of precision.
This format has no use for a precision specifier, because by definition, we always want full precision. (You can provide one if you like, because all the standard numeric formats follow a common pattern, including an optional precision. This format supports the common syntax rules, it just ignores the precision.) The framework will use the most compact form it can to achieve the round-trip behavior. Example 10-20 shows this format in use.
double amount = 0.684848; string text = amount.ToString("R"); Console.WriteLine(text);
This produces the following output:
Learn more about this topic from Programming C# 4.0.
This bestselling tutorial shows you how to build web, desktop, and rich Internet applications using C# 4.0 with .NET's database capabilities, UI framework (WPF), extensive communication services (WCF), and more. The sixth edition covers the latest enhancements to C#, as well as the fundamentals of both the language and framework. You'll learn concurrent programming with C# 4.0, and how to use .NET tools such as the Entity Framework for easier data access, and the Silverlight platform for browser-based RIA development.