Chapter 5. Numbers

Numbers form an integral part of many applications. Although seemingly a simple topic, when you consider how the world’s many cultures represent numbers, and even how computers can represent numbers, the topic is not so simple after all. This chapter covers a lot of tips that are useful in many applications.

Decide Between Float, Double, and Decimal

Solution: Deciding which floating-point type to use is application dependent, but here are some things to think about:

• How big do the numbers need to be? Are they on the extreme, either positive or negative?

• How much precision do you need? Is seven digits enough? Sixteen? Need more?

• Do you have memory requirements that influence which you choose?

• Are the numbers coming from a database that already specifies the size it’s using? This should match what you choose so you don’t lose data.

Given these questions, Table 5.1 demonstrates the differences.

Table 5.1 Floating-Point Types

image

Largest exact integer means the largest integer value that can be represented without loss of precision. These values should be kept in mind when you’re converting between integer and floating-point types.

Note

For any calculation involving money or finances, the Decimal type should always be used. Only this type has the appropriate precision to avoid critical rounding errors.

Use Enormous Integers (BigInteger)

Solution: .NET 4 ships with the BigInteger class, located in the System.Numerics namespace (you will need to add a reference to this assembly). With it, you can do arbitrarily large integer math, such as the following:

BigInteger a = UInt64.MaxValue;
BigInteger b = UInt64.MaxValue;

//this is a really, really big number
BigInteger c = a * b;

BigInteger is immutable, which means that calling

BigInteger a = 1;
a++;

will result in the creation of a second BigInteger object, assigning it back to a. Although this is transparent to you in practice, you should be aware that it has potential performance implications.

BigInteger has many static methods, such as the typical Parse(), TryParse(), and ToString() methods (which support hex format as well) found in other integer types, as well as mathematical helpers such as Pow(), Log(), Log10(), and others.

BigInteger’s ToString() method accepts the usual format specifiers ("C", "N0", and so on), plus one more: "R". With all except "R", only 50 digits of integer precision are output, with the rest replaced by zeroes. With "R", all digits are preserved, but you can’t apply other formatting (such as digit separators). The difference is shown here:

image

Output:

image

Using the output of the "R" format, you could of course perform your own manual formatting.

BigInteger also includes some instance helper properties, such as IsPowerOfTwo and IsEven, to simplify some tasks.

Use Complex Numbers

Solution: Use the System.Numerics.Complex class to represent imaginary numbers. Complex wraps both the real and imaginary parts using doubles as the underlying format.

image

This has the output

image

Format a Complex Number

While (a, b) is a valid format for imaginary numbers, it is common to see it written a+bi instead. This is easily accomplished with a custom formatter (as you learned in Chapter 2, “Creating Versatile Types”).

image

image

Example usage:

image

This gives the output:

Sqrt(c) = 0.00 + 1.00i

Format Numbers in a String

Solution: The .NET Framework provides a wealth of number formatting options. There are so many options that it can be confusing at first. This section highlights a few of the more interesting options, including a table summarizing many examples.

Note

If you call ToString() with no arguments, the current thread’s culture will be assumed. It is often in your best interest to explicitly specify the culture.

Format a Number for a Specific Culture

Culture defines how the formatted number looks. Depending on the culture, some or all of these variables could change:

Decimal separator—1.5 or 1,5?

Digit separator—1,000,000 or 1.000.000?

Digit grouping—In some cultures (such as India), digits are not grouped by threes.

Currency symbol—U.S. Dollar ($), British Pound (£), Euro (€), Japanese Yen (¥).

Default number of decimal places for certain formats

CultureInfo implements IFormatProvider, so any method that takes it can accept a culture. Here are some examples:

image

This code produces the following output:

1 234 567,89
12,34,567.89

Note

Use the invariant culture whenever you need to store data for the application to consume. This is vitally important because it is impossible to accurately parse numbers when the format is not known beforehand. Alternatively, use binary formats because culture information is not relevant.

Print Numbers in Hexadecimal

Note that the “0x” that is typically prepended to hexadecimal numbers must be added by you. You can also specify how many digits you want shown (they will be zero-padded).

image

The output is as follows:

3039
0x00003039

Group Digits

To group digits, use the "N" format option, as shown here:

image

This produces the following output:

12,345

Print Leading Zeros

You can print leading zeros with the D and X format strings. The number specifies the total number of digits to output. If this number is greater than the number of digits in the number, the output is padded with zeros; otherwise, the number is output normally. Here’s an example:

image

And here’s the output:

00012345

Specify Number of Decimal Places

You can specify the number of decimal places with the C, E, F, G, N, and P format strings:

image

The output is as follows (note the rounding):

12345.679

Use a Custom Format String for Finer Control

Solution: Use custom format specifiers.

image

Here’s the output:

00012345.68
00,012,345.68
0,00,12,345.68
(00012345.679)
nothing!

Number Format Strings Summary

Table 5.2 provides a summary of number format strings.

Table 5.2 Number Format Strings

image

Convert a String to a Number

Solution: You can choose between Parse, which throws an exception if anything goes wrong, or TryParse, which is guaranteed not to throw an exception. I think you’ll find that TryParse makes sense in most situations because of the performance implications when using exceptions.

image

Note that goodStr has digit separators, decimal points, spaces, and a negative sign. By default, TryParse will accept all of these. If you know the format of the number, you can restrict the format using the NumberStyles enumeration.

image

Note

If you don’t specify a culture when you parse, the current thread’s culture is assumed. To specify another culture, do this:

image

Parse Hex Number

To parse hexadecimal number strings, you must remove any “0x” that precedes the number before calling TryParse().

image

The following output is produced:

Parsed 0x3039 to value 12345

Convert Between Number Bases

Solution: You can’t convert numbers, per se, to different bases. They are, after all, always represented as binary in the computer. You are merely showing a representation of that number—in other words, a string. How to do it depends on whether you want the typical “computer” bases or an arbitrary one.

Convert from Base-10 to Base-2, -8, or -16

Thankfully, if you want a standard base such as 2, 8, or 16, the functionality is already built into the Framework:

image

destStr will contain the value 64, the hexadecimal equivalent of 100 in base-10.

Convert from Base-10 to an Arbitrary Base

What if you need a really strange number base, such as 5 or 99? For that, the following code will help:

image

Note that although this can accept any destination base, you will need to define any characters for digit values up to destBase - 1.

Convert to Base-10

The following code converts a string representation of a number in an arbitrary base (up to 16 in this example) to base-10:

image

Note

If you need to convert between two arbitrary bases, it is easier to first convert to base-10, then convert to the other base using a combination of the methods given.

Convert a Number to Bytes (and Vice Versa)

Solution: The handy BitConverter class will help you.

Int32 num = 13;
byte[] bytes = BitConverter.GetBytes(13);

Here’s how to convert back:

Int32 num = BitConverter.ToInt32(bytes);

BitConverter can work on all number types except, oddly enough, Decimal. For that, you need this code:

image

Note

Whenever you are converting numbers to bytes (and vice versa), you need to be concerned about endianness, which is merely the order of the bytes in memory. Numbers are said to be little endian if they start with the least-significant byte first. Big endian values start with the most-significant byte. Most personal computers these days are little endian, but the BitConverter class provides a handy static property called IsLittleEndian that can tell you about the current hardware.

Determine if an Integer Is Even

Solution: An integer is even if the least-significant bit is 0.

image

Determine if an Integer Is a Power of 2 (aka, A Single Bit Is Set)

Solution: The solution relies on the fact that integers are twos-complement encoded and that a power of 2 has only a single 1 bit.

image

The check for 0 could be omitted if the function needed to be called a lot and you could ensure that 0 will never be passed to it.

Determine if a Number Is Prime

Solution: A prime number is divisible only by 1 and itself. A popular solution is this example:

image

See Chapter 23, “Threads and Asynchronous Programming,” for an example of splitting up the work IsPrime() is doing across multiple processors.

Count the Number of 1 Bits

Solution: There are many solutions to this problem, but here’s my favorite:

image

This runs in time proportion to the number of 1 bits set in number.

Convert Degrees and Radians

Solution: This code is common in many types of graphical applications, where degrees make sense to humans, but many graphics APIs handle radians. If your application uses these a lot, you might consider making them extension methods on double.

See the RadiansAndDegrees sample project to see this code in action in conjunction with the mouse.

image

Pi, as well as other useful constants and mathematical functions, is in the Math class.

Round Numbers

Solution: The Math class provides a handy function that performs rounding for you. You can specify the decimal precision as well as the type of midpoint rounding (the behavior when on a middle value such as .5, .05, and so on). If no rounding type is specified, the default is MidpointRounding.ToEven, also known as banker’s rounding.

Table 5.3 Rounding Methods

image

Here are some examples (the sample output is from the RoundNumbers example in the chapter’s source code).

Table 5.4 summarizes the results from calling with Math.Round() with various arguments.

Table 5.4 Rounding Samples

image

Round Numbers to Nearest Multiple

Scenario/Problem: In many graphical editors, a common feature is to restrict mouse positions to gridlines. This makes the user’s job easier because they don’t have to make ultra-precise mouse movements when editing shapes. For example, if the mouse were at (104, 96) and you wanted the values snapped to a 5-by-5 pixel grid, you could change them to (105, 95).

Solution: This is easily accomplished with the following code:

image

Table 5.5 demonstrates this algorithm when rounding/snapping values to the nearest multiple of ten.

Table 5.5 Snap to Multiples of 10

image

See the SnapToGrid project in the code for this chapter for an example of snapping actual mouse input to a grid.

If you want to round to a floating-point value (say, the nearest 0.5), you need to massage the inputs and outputs of the preceding function slightly, as shown here:

image

The precision argument specifies how many decimal points your input values can contain. This is important because the multiple needs to be scaled to an integer so that the math can work correctly. Table 5.6 uses a precision of two.

Table 5.6 Snap to Multiples of 0.5

image

Generate Better Random Numbers

Solution: The standard way to generate random numbers is with the System.Random class, which produces numbers that appear random, based on a seed value (often the current time). That usage is shown here:

Random rand = new Random();
Int32 randomNumber = rand.Next();

If you just need an arbitrary number for noncryptographic purposes, this is perfectly fine. However, if you are doing anything security related, you should never use Random. Instead, use System.Security.Cryptography.RNGCryptoServiceProvider. Here’s an example:

image

Note

Both methods of generating random numbers are actually pseudorandom (that is, not truly random). Pseudorandom numbers are based on some seed, or known data. System.Random uses a seed value (often the current time), and RNGCryptoServiceProvider uses a combination of processor info, operating system counters, cycle counters, time, and other information to generate cryptographically secure bytes. True randomness can generally be achieved only through more complicated phenomena (static noise, mouse movement, network traffic patterns, and so on).

Note

You may not always want or need cryptographically secure random numbers. For instance, if you just need an arbitrary number and there are no security considerations, using System.Random might be preferable for easier testing because you can reproduce the desired numbers at will by giving a known seed value.

Generate Unique IDs (GUIDs)

Solution: Use the System.Guid class to generate 128 bytes of data that has a very high probability of being unique across all computers and all networks, for all time.

Guid g = Guid.NewGuid();
Console.WriteLine("GUID: {0}", g);

produces the output:

GUID: ea8b716c-892a-4bed-b918-0d454c1b8474

GUIDs are used all over in databases and operating systems to uniquely identify records and components.

Note

GUIDs are generated from a combination of hardware information and the current time, but the generation is one way; that is, you cannot infer any information about the hardware from a given GUID.

The Guid class provides Parse() and TryParse()methods to convert strings to GUID objects. There are a few common string representations of GUIDs, so there are also ParseExact() and TryParseExact() methods.

image

This produces the output:

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset