C# decimal and double - c#

From what I understand decimal is used for precision and is recommended for monetary calculations. Double gives better range, but less precision and is a lot faster than decimal.
What if I have time and rate, I feel like double is suited for time and decimal for rate. I can't mix the two and run calculations without casting which is yet another performance bottleneck. What's the best approach here? Just use decimal for time and rate?

Use double for both. decimal is for currency or other situations where the base-10 representation of the number is important. If you don't care about the base-10 representation of a number, don't use decimal. For things like time or rates of change of physical quantities, the base-10 representation generally doesn't matter, so decimal is not the most appropriate choice.
The important thing to realize is that decimal is still a floating-point type. It still suffers from rounding error and cannot represent certain "simple" numbers (such as 1/3). Its one advantage (and one purpose) is that it can represent decimal numbers with fewer than 29 significant digits exactly. That means numbers like 0.1 or 12345.6789. Basically any decimal you can write down on paper with fewer than 29 digits. If you have a repeating decimal or an irrational number, decimal offers no major benefits.

The rule of thumb is to use the type that is more suitable to the values you will handle. This means that you should use DateTime or TimeSpan for time, unless you only care about a specific unit, like seconds, days, etc., in which case you can use any integer type. Usually for time you need precision and don't want any error due to rounding, so I wouldn't use any floating point type like float or double.
For anything related to money, of course you don't want any rounding error either, so you should really use decimal here.
Finally, only if for some very specific requirements you need absolute speed in a calculation that is done millions of times and for which decimal happens not to be fast enough, only then I would think of using another faster type. I would first try with integer values (maybe multiplying your value by a power of 10 if you have decimals) and only divide by this power of 10 at the end. If this can't be done, only then I would think of using a double. Don't do a premature optimization if you are not sure it's needed.

Related

Inaccuracy of decimal in .NET

Yesterday during debugging something strange happened to me and I can't really explain it:
So maybe I am not seeing the obvious here or I misunderstood something about decimals in .NET but shouldn't the results be the same?
decimal is not a magical do all the maths for me type. It's still a floating point number - the main difference from float is that it's a decimal floating point number, rather than binary. So you can easily represent 0.3 as a decimal (it's impossible as a finite binary number), but you don't have infinite precision.
This makes it work much closer to a human doing the same calculations, but you still have to imagine someone doing each operation individually. It's specifically designed for financial calculations, where you don't do the kind of thing you do in Maths - you simply go step by step, rounding each result according to pretty specific rules.
In fact, for many cases, decimal might work much worse than float (or better, double). This is because decimal doesn't do any automatic rounding at all. Doing the same with double gives you 22 as expected, because it's automatically assumed that the difference doesn't matter - in decimal, it does - that's one of the important points about decimal. You can emulate this by inserting manual Math.Rounds, of course, but it doesn't make much sense.
Decimal can only store exactly values that are exactly representable in decimal within its precision limit. Here 22/24 = 0.91666666666666666666666... which needs infinite precision or a rational type to store, and it does not equal to 22/24 after rounding anymore.
If you do the multiplication first then all the values are exactly representable, hence the result you see.
By adding brackets you are making sure that the division is calculated before the multiplication. This subtlely looks to be enough to affect the calculation enough to introduce a floating precision issue.
Since computers can't actually produce every possible number, you should make sure you factor this into your calculations
While Decimal has a higher precision than Double, its primary useful feature is that every value precisely matches its human-readable representation. While the fixed-decimal types which are available in some languages can guarantee that neither addition or subtraction of two matching-precision fixed-point values, nor multiplication of a fixed-point type by an integer, will ever cause rounding error, and while "big-decimal" types such as those found in Java can guarantee that no multiplication will ever cause rounding errors, floating-point Decimal types like the one found in .NET offers no such guarantees, and no decimal types can guarantee that division operations can be completed without rounding errors (Java's has the option to throw an exception in case rounding would be necessary).
While those deciding to make Decimal be a floating-point type may have intended that it be usable either for situations requiring more digits to the right of the decimal point or more to the left, floating-point types, whether base-10 or base-2, make rounding issues unavoidable for all operations.

Rounding a financial value of double datatype accurately to 2 decimal places in C#

We are using these double values to represent bill amounts. I read that it is better to use 'decimal" datatype rather than double to minimize rounding errors. But Its a very big project and changing all dataypes to decimal is a herculean task.
SO we tried Math.Round with both kinds of midpoint rounding but nothing works. There is some kind of an error.
Is there anyway to make the rounding upto 2 decimal places accurately?
EDIT:
Sorry for not providing examples. The problem is once the values (totally there are 24 "double"values)get added before rounding(they were upto 15 places originally), the summed value comes to 18167.04 which is desired. But when they are rounded to 2 decimal places (using Math.Round or Math.Round with MidpointRounding), the summed value is 18167.07 (differs by .03).
Using Decimal datatype is apt for monetary calculations but since it a huge project, for now, implementing the change in datatype is a task.
No way of Rounding works.
Is the problem really with the datatype here or because of rounding?
WIll the same Rounding method work if decimal datatype is used?
Floating point accuracy is not defined by decimal points, it is defined by significant digits. 123456789123456789.0 is no more or less accurate than 0.123456789123456789.
There is a problem that frequently occurs when dealing with financial values in programming:
Float values do not tanslate well to decimal fractions.
Many developers tend to think of float values as decimal fractions, because they are mostly represented by these, when converting them to strings and vice versa. This is not the case. Float values have their fractional part stored as binary fraction, as descibed here.
This makes float values (and their calulations) being slightly askew from their decimal representations.
One way out of this problem, is (as you stated) to use the data type decimal, that was construced to do calculations that have to translate directly to decimal fractions (as financial calculations do).
Another would be to round all results of floating point calculations, before displaying or storing them. For financial calculations one should use:
Math.Round([your value here], 2, MidpointRounding.AwayFromZero);
I would advise to opt for the former, whenever possible. It spares many a headache, when calculation is done. With the rounding approach, one has to take rounding errors into account at many points. It may be a big task to convert an existing project to decimal, but it will most propably pay out in the long run (if it is possible, in the fist place)...
Regarding your edit:
This problem arises, because the rounding errors get cumulated if you round before you sum. You could opt rounding the sum after summing up the un-rounded values. Whether you use double or decimal is irellevant in this regard, as this applies to both.
Whether this is allowed, depends on the type of financial calculation you do.
If the the summands appear in printed form on e.g. an invoice, then it is most propably not allowed to do so.

Which type to save percentages

Is it appropriate to use the double type to store percentage values (for example a discount percentage in a shop application) or would it be better to use the decimal type?
Floating-point types (float and double are particularly ill-suited to financial applications.
Financial calculations are almost always decimal, while floating-point types are almost always binary. Many common values that are easy to represent in decimal are impossible to represent in binary. For example, 0.2d = 0.00110011...b. See http://en.wikipedia.org/wiki/Binary_numeral_system#Fractions_in_binary for a good discussion.
It's also worth talking about how you're representing prices in your system. decimal is a good choice, but floating point is not, for reasons listed above. Because you believe in Object Oriented Programming, you're going to wrap that decimal in a new Money type, right? A nice treatment of money comes in Kent Beck's Test Driven Development by Example.
Perhaps you will consider representing percentages as an integer, and then dividing by 100 every time you use it. However, you are setting yourself up for bugs (oops, I forgot to divide) and future inflexibility (customer wants 1/10ths of a percent, so go fix every /100 to be /1000. Oops, missed one - bug.)
That leaves you with two good options, depending on your needs. One is decimal. It's great for whole percentages like 10%, but not for things like "1/3rd off today only!", as 1/3 doesn't represent exactly in decimal. You'd like it if buying 3 of something at 1/3rd off comes out as a whole number, right?
Another is to use a Fraction type, which stores an integer numerator and denominator. This allows you to represent exact values for all rational numbers. Either implement your own Fraction type or pick one up from a library (search the internet).
You can probably get away with saving the discount percentage as an integer. Just store 10 or 25 or whatever, and when you need to work out the price of something:
newprice = price * discount / 100
decimal does come at a performance cost, but it's usually worth it for financial uses. The reason it has low performance (the worst of all numeric types) is that it doesn't map directly to a hardware type. That means it requires more of the work to be done in software.
Note that it is not only an issue of size. decimal is an integer scaled by a power of 10, while the float and double types are scaled by powers of 2. That means terminating decimal values like 0.1 can be exactly represented using decimal, while they are non-terminating (and thus rounded) for float and double.
I try to avoid floating-point whenever possible. Nothing irritates me more than having .25 not equal to .25, something that happens when you start dealing with them.
A regular float should be fine, unless you need accuracy to like, five decimal places.

Is casting narrow types to wider types to save memory and keep high-precision calculations a terrible idea?

I'm dealing with financial data, so there's a lot of it and it needs to be relatively high-precision (64bit floating point or wider).
The standard practice around my workplace seems to be to represent all of it as the c# decimal type which is a 128bit wide floating point specifically created to support round-off free base10 operations.
Since 64bit is wide enough to maintain the representative precision, is it ridiculous to cast the data to the wider type for all calculations (mult,div,add,etc) and then back to 64bit for sitting in memory (which is where it spends of most if its time)?
For reference: memory is definitely the limiting resource here.
The point of using decimal (128 bits) over double (64 bits) and float (32 bits) isn't usually to do with the size. It's to do with the base. While double and float are floating binary point types, decimal is a floating decimal point type - and it's that feature that lets it represent numbers like 0.1 exactly where float/double can't.
There's no conceptual reason why we couldn't haven't a 64-bit decimal type, and in many cases that would indeed be enough - but until such a type comes along or you write it yourself, please don't use the "shorter" (and binary floating point) types of float/double for financial calculations. If you do, you're asking for trouble.
If you're suggesting writing a storage type which can convert to/from decimal and is still a floating decimal type, that sounds like a potentially good idea even without it being able to do any calculations. You'll need to be very careful when you think about what to do if you're ever asked to convert a decimal value which you can't represent exactly though. I'd be interested in seeing such a type, to be honest. Hmm...
(As other answers have indicated, I'd really make sure that it's the numbers which are taking up the memory before doing this, however. If you don't need to do it, there's little point in introducing the extra complexity speculatively.)
64bit floating point cannot maintain precision of financial data. It is not a matter of space, it is a matter of which number system the data types use; double uses base-2, decimal is base-10, and base-2 cannot represent exact base-10 decimals even if it had 1000 bits of precision.
Don't believe me? Run this:
double d = 0.0;
for (int i = 0; i < 100; i++)
d += 0.1;
Console.WriteLine(d);
> 9.99999999999998
If you need base-10 calculations you need the decimal type.
(Edit: damn, beaten by Jon Skeet again...)
If the decimal type really is the bottleneck, you can use a long number of pennies (or 1/8 cent or whatever your unit is) instead of decimal dollars.
You should use a profiler to see what objects are taking up a lot of memory. If your decimal objects are the culprit, then I would say yes go after them. Otherwise you are just making guesses. Profiler will tell you for sure.
It is perfectly reasonable to store your numbers at 64 bit, cast them to the decimal type for calculations, and cast the result back to 64 bit, if you don't mind the performance hit.
We require this level of precision where I work, so this is exactly what we do here. We take a two orders of magnitude hit in speed by doing the cast, but we never have to worry about large errors in the floating point arithmetic. Without the cast, the calculation can be wildly inaccurate, depending on the range of the numbers and the type of calculation being performed.
For more on floating point arithmetic, and why errors can creep into your calculations, see "What Every Computer Scientist Should Know About Floating-Point Arithmetic" at http://docs.sun.com/source/806-3568/ncg_goldberg.html
This seems perfectly sane, if 64 bit floating point is truly enough to represent the precision you want. The extra precision decimal is, as you say, often used purely to minimize cumulative errors over multiple operations.
As most of the other posts have already pointed out, converting between 128-bit decimal and 64-bit floating point representations is not a conversion that will always maintain accuracy.
However, if you are dealing with the prices of financial shares, you could consider representing them as ints (the number of pennies) rather than as decimal value (the number of fractional dollars). Perform all financial calculations in pennies and then only expose them to the outside world as decimals when requested.
Another approach may be to improve the algorithmic efficiency of your system rather than "compressing" the storage type. Do you really need all that data in memory at once? Can you virtualize it somehow?
If not, given the volume of data you are managing, you may want to look into organizing the data in a way the reduces the redundancy. For example, not every share has a historic price back in time (some companies don't exist far enough back in time). So orgnize your data as a dictionary of stock prices by day (or year) rather than as a tabular structure for each stock. There may be other alternatives depending on how your data is available and how you intend to perform calculations with it.
You need to do the numerical analysis to see if the practice (of keeping 128 bits) is ridiculous, or just lazy, or really necessary.
Is "just add more memory" an acceptable answer?
How much cost is involved in properly coding and testing the proposed approach of moving the values between these representations. Compare that cost with shovelling more memory into a machine with the app running as a 64 bit process.
From MSDN decimal: There is no implicit conversion between floating-point types and the decimal type; therefore, a cast must be used to convert between these two types.
It looks like it is REQUIRED to do the cast in the case you are using.
That being said, it is very important that you understand what most other people here are posing about in regards to the problems of representing currency in floating point.
You may way to consider creating/finding a 64bit BCD (Binary Coded Decimal) implementation that you can use for your system.
Same doubles converted to decimals and then converted to byte[] and then compressed takes c.2x less space (I have just tested this with several compression libraries: Blosc with default, lz4, zlib with or without shuffle, with shuffle decimals are the best).
One option is to store compressed decimals in memory or on disk, since CPUs are starving nowdays. See a number of presentations here: http://blosc.org/docs/

When should I use double instead of decimal?

I can name three advantages to using double (or float) instead of decimal:
Uses less memory.
Faster because floating point math operations are natively supported by processors.
Can represent a larger range of numbers.
But these advantages seem to apply only to calculation intensive operations, such as those found in modeling software. Of course, doubles should not be used when precision is required, such as financial calculations. So are there any practical reasons to ever choose double (or float) instead of decimal in "normal" applications?
Edited to add:
Thanks for all the great responses, I learned from them.
One further question: A few people made the point that doubles can more precisely represent real numbers. When declared I would think that they usually more accurately represent them as well. But is it a true statement that the accuracy may decrease (sometimes significantly) when floating point operations are performed?
I think you've summarised the advantages quite well. You are however missing one point. The decimal type is only more accurate at representing base 10 numbers (e.g. those used in currency/financial calculations). In general, the double type is going to offer at least as great precision (someone correct me if I'm wrong) and definitely greater speed for arbitrary real numbers. The simple conclusion is: when considering which to use, always use double unless you need the base 10 accuracy that decimal offers.
Edit:
Regarding your additional question about the decrease in accuracy of floating-point numbers after operations, this is a slightly more subtle issue. Indeed, precision (I use the term interchangeably for accuracy here) will steadily decrease after each operation is performed. This is due to two reasons:
the fact that certain numbers (most obviously decimals) can't be truly represented in floating point form
rounding errors occur, just as if you were doing the calculation by hand. It depends greatly on the context (how many operations you're performing) whether these errors are significant enough to warrant much thought however.
In all cases, if you want to compare two floating-point numbers that should in theory be equivalent (but were arrived at using different calculations), you need to allow a certain degree of tolerance (how much varies, but is typically very small).
For a more detailed overview of the particular cases where errors in accuracies can be introduced, see the Accuracy section of the Wikipedia article. Finally, if you want a seriously in-depth (and mathematical) discussion of floating-point numbers/operations at machine level, try reading the oft-quoted article What Every Computer Scientist Should Know About Floating-Point Arithmetic.
You seem spot on with the benefits of using a floating point type. I tend to design for decimals in all cases, and rely on a profiler to let me know if operations on decimal is causing bottlenecks or slow-downs. In those cases, I will "down cast" to double or float, but only do it internally, and carefully try to manage precision loss by limiting the number of significant digits in the mathematical operation being performed.
In general, if your value is transient (not reused), you're safe to use a floating point type. The real problem with floating point types is the following three scenarios.
You are aggregating floating point values (in which case the precision errors compound)
You build values based on the floating point value (for example in a recursive algorithm)
You are doing math with a very wide number of significant digits (for example, 123456789.1 * .000000000000000987654321)
EDIT
According to the reference documentation on C# decimals:
The decimal keyword denotes a
128-bit data type. Compared to
floating-point types, the decimal type
has a greater precision and a smaller
range, which makes it suitable for
financial and monetary calculations.
So to clarify my above statement:
I tend to design for decimals in all
cases, and rely on a profiler to let
me know if operations on decimal is
causing bottlenecks or slow-downs.
I have only ever worked in industries where decimals are favorable. If you're working on phsyics or graphics engines, it's probably much more beneficial to design for a floating point type (float or double).
Decimal is not infinitely precise (it is impossible to represent infinite precision for non-integral in a primitive data type), but it is far more precise than double:
decimal = 28-29 significant digits
double = 15-16 significant digits
float = 7 significant digits
EDIT 2
In response to Konrad Rudolph's comment, item # 1 (above) is definitely correct. Aggregation of imprecision does indeed compound. See the below code for an example:
private const float THREE_FIFTHS = 3f / 5f;
private const int ONE_MILLION = 1000000;
public static void Main(string[] args)
{
Console.WriteLine("Three Fifths: {0}", THREE_FIFTHS.ToString("F10"));
float asSingle = 0f;
double asDouble = 0d;
decimal asDecimal = 0M;
for (int i = 0; i < ONE_MILLION; i++)
{
asSingle += THREE_FIFTHS;
asDouble += THREE_FIFTHS;
asDecimal += (decimal) THREE_FIFTHS;
}
Console.WriteLine("Six Hundred Thousand: {0:F10}", THREE_FIFTHS * ONE_MILLION);
Console.WriteLine("Single: {0}", asSingle.ToString("F10"));
Console.WriteLine("Double: {0}", asDouble.ToString("F10"));
Console.WriteLine("Decimal: {0}", asDecimal.ToString("F10"));
Console.ReadLine();
}
This outputs the following:
Three Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 599999.9999886850
Decimal: 600000.0000000000
As you can see, even though we are adding from the same source constant, the results of the double is less precise (although probably will round correctly), and the float is far less precise, to the point where it has been reduced to only two significant digits.
Use decimal for base 10 values, e.g. financial calculations, as others have suggested.
But double is generally more accurate for arbitrary calculated values.
For example if you want to calculate the weight of each line in a portfolio, use double as the result will more nearly add up to 100%.
In the following example, doubleResult is closer to 1 than decimalResult:
// Add one third + one third + one third with decimal
decimal decimalValue = 1M / 3M;
decimal decimalResult = decimalValue + decimalValue + decimalValue;
// Add one third + one third + one third with double
double doubleValue = 1D / 3D;
double doubleResult = doubleValue + doubleValue + doubleValue;
So again taking the example of a portfolio:
The market value of each line in the portfolio is a monetary value and would probably be best represented as decimal.
The weight of each line in the portfolio (= Market Value / SUM(Market Value)) is usually better represented as double.
Use a double or a float when you don't need precision, for example, in a platformer game I wrote, I used a float to store the player velocities. Obviously I don't need super precision here because I eventually round to an Int for drawing on the screen.
In some Accounting, consider the possibility of using integral types instead or in conjunction. For example, let say that the rules you operate under require every calculation result carry forward with at least 6 decimal places and the final result will be rounded to the nearest penny.
A calculation of 1/6th of $100 yields $16.66666666666666..., so the value carried forth in a worksheet will be $16.666667. Both double and decimal should yield that result accurately to 6 decimal places. However, we can avoid any cumulative error by carrying the result forward as an integer 16666667. Each subsequent calculation can be made with the same precision and carried forward similarly. Continuing the example, I calculate Texas sales tax on that amount (16666667 * .0825 = 1375000). Adding the two (it's a short worksheet) 1666667 + 1375000 = 18041667. Moving the decimal point back in gives us 18.041667, or $18.04.
While this short example wouldn't yield a cumulative error using double or decimal, it's fairly easy to show cases where simply calculating the double or decimal and carrying forward would accumulate significant error. If the rules you operate under require a limited number of decimal places, storing each value as an integer by multiplying by 10^(required # of decimal place), and then dividing by 10^(required # of decimal places) to get the actual value will avoid any cumulative error.
In situations where fractions of pennies do not occur (for example, a vending machine), there is no reason to use non-integral types at all. Simply think of it as counting pennies, not dollars. I have seen code where every calculation involved only whole pennies, yet use of double led to errors! Integer only math removed the issue. So my unconventional answer is, when possible, forgo both double and decimal.
If you need to binary interrop with other languages or platforms, then you might need to use float or double, which are standardized.
Depends on what you need it for.
Because float and double are binary data types you have some diifculties and errrors in the way in rounds numbers, so for instance double would round 0.1 to 0.100000001490116, double would also round 1 / 3 to 0.33333334326441. Simply put not all real numbers have accurate representation in double types
Luckily C# also supports the so-called decimal floating-point arithmetic, where numbers are represented via the decimal numeric system rather than the binary system. Thus, the decimal floating point-arithmetic does not lose accuracy when storing and processing floating-point numbers. This makes it immensely suited to calculations where a high level of accuracy is needed.
Note: this post is based on information of the decimal type's capabilities from http://csharpindepth.com/Articles/General/Decimal.aspx and my own interpretation of what that means. I will assume Double is normal IEEE double precision.
Note2: smallest and largest in this post reffer to the magnitude of the number.
Pros of "decimal".
"decimal" can represent exactly numbers that can be written as (sufficiently short) decimal fractions, double cannot. This is important in financial ledgers and similar where it is important that the results exactly match what a human doing the calculations would give.
"decimal" has a much larger mantissa than "double". That means that for values within it's normalised range "decimal" will have a much higher precision than double.
Cons of decimal
It will be Much slower (I don't have benchmarks but I would guess at least an order of magnitude maybe more), decimal will not benefit from any hardware acceleration and arithmetic on it will require relatively expensive multiplication/division by powers of 10 (which is far more expensive than multiplication and dividion by powers of 2) to match the exponent before addition/subtraction and to bring the exponent back into range after multiplication/division.
decimal will overflow earlier tha double will. decimal can only represent numbers up to ±296-1 . By comparision double can represent numbers up to nearly ±21024
decimal will underflow earlier. The smallest numbers representable in decimal are ±10-28 . By comparision double can represent values down to 2-149 (approx 10-45) if subnromal numbers are supported and 2-126 (approx 10-38) if they are not.
decimal takes up twice as much memory as double.
My opinion is that you should default to using "decimal" for money work and other cases where matching human calculation exactly is important and that you should use use double as your default choice the rest of the time.
Use floating points if you value performance over correctness.
Choose the type in function of your application. If you need precision like in financial analysis, you have answered your question. But if your application can settle with an estimate your ok with double.
Is your application in need of a fast calculation or will he have all the time in the world to give you an answer? It really depends on the type of application.
Graphic hungry? float or double is enough. Financial data analysis, meteor striking a planet kind of precision ? Those would need a bit of precision :)
Decimal has wider bytes, double is natively supported by CPU. Decimal is base-10, so a decimal-to-double conversion is happening while a decimal is computed.
For accounting - decimal
For finance - double
For heavy computation - double
Keep in mind .NET CLR only supports Math.Pow(double,double). Decimal is not supported.
.NET Framework 4
[SecuritySafeCritical]
public static extern double Pow(double x, double y);
A double values will serialize to scientific notation by default if that notation is shorter than the decimal display. (e.g. .00000003 will be 3e-8) Decimal values will never serialize to scientific notation. When serializing for consumption by an external party, this may be a consideration.

Categories