I am attempting to validate a range dollar amount. The value must be less than (not equal to) the input currency amount, which can be represented in multiple currencies.
How can I represent this value? Should I convert to decimal and subtract 0.01? If that doesn't work, why not? I'm using C#.
I'm possibly over-thinking an obvious solution, so an "Uhhhh do 'X' " type of response would not surprise me.
Thanks for any insight.
Seeing as how you say it can be in different currencies, Are the values you are comparing both of the same currency? If not then your range will need to be even more dynamic as some foreign currencies may result in a larger number than your initial value.
So for example if you are comparing a USD 100 to YEN which would be about 92 yen per dollar for 9200 yen (on average) for today. that would not really match up. It's best to compare same currency to same currency for ranges.
Also you never said what your range is. You are only talking about your Maximum. What's your min? Is it Zero what if you have a negative amount? If this was a trading application you could have positive and negatives.
Your best bet if you have a maximum value is to convert your entered value to whatever similar currency you have and then adjust validate that it's less than or equal to your max value. Which might be better in a codebehind validation vs a range validator.
So lets assume you are using a trading app. You have a Trade Limit say 1000 USD. The user is doing an international trade in YEN. When you go to do your checks you will have to get the FX Rate for the day, divide it into your YEN entered amount and verify that it's less than or equal to your limit. In a trading scenario you could use an ABS value so that a Buy or Sell net out to the same 1000 limit. Since this is a bit more complicated than a simple range check I would personally recommend doing your own validations either with jQuery / AJAX or just plain code behind.
May make your life much easier.
Related
My team is working with financial software that exposes monetary values as C# floating point doubles. Occasionally, we need to compare these values to see if they equal zero, or fall under a particular limit. When I noticed unexpected behavior in this logic, I quickly learned about the rounding errors inherent in floating point doubles (e.g. 1.1 + 2.2 = 3.3000000000000003). Up until this point, I have primarily used C# decimals to represent monetary values.
My team decided to resolve this issue by using the epsilon value approach. Essentially, when you are compare two numbers, if the difference between those two numbers is less than epsilon, they are considered equal. We implemented this approach in a similar way as described in the article below:
https://www.codeproject.com/Articles/383871/Demystify-Csharp-floating-point-equality-and-relat
Our challenge has been determining an appropriate value for epsilon. Our monetary values can have up to 3 digits to the right of the decimal point (scale = 3). This means that the largest epsilon we could use is .0001 (anything larger and the 3rd digit gets ignored). Since epsilon values are supposed to be small, we decided to move it out one more decimal point to .00001 (just to be safe, you could say). C# doubles have a precision of at least 15 digits, so I believe this value of epsilon should work if the number to the left of the decimal point is less or equal to 10 digits (15 - 5 = 10, where 5 is the number of digits epsilon is to the right of the decimal point). With 10 digits, we can represent values into the billions, up to 9,999,999,999.999. It's possible that we may have numbers in the hundreds of millions, but we don't expect to go into the billions, so this limit should suffice.
Is my rationale for choosing this value of epsilon correct? I found a lot of resources that discuss this approach, but I couldn’t find many resources that provide guidance on choosing epsilon.
Your reasoning seems sound, but as you have already discovered it is a complicated issue. You might want to read What Every Computer Scientist Should Know About Floating-Point Arithmetic. You do have a minimum of 15 digits of precision using 64 bit doubles. However, you will also want to validate your inputs as floats can contain Nan, +/- Infinity, negative zero and a considerably larger "range" than 15 decimal digits. If someone hands your library a value like 1.2E102, should you process it or consider it out of range? Ditto with very small values. Garbage In, Garbage out, but it might be nice if you code detected the "smell" of garbage and at very least logged it.
You might also want to consider providing a property for setting precision as well as different forms of rounding. That depends largely on the specifications you are working with. You might also want to determine if these values can represent currencies other than dollars (1 dollar is currently >112 yen).
Long and the short of it choosing your epsilon a digit below your needs (so four digits to the right of the decimal) is sound and gives you a digit to use for consistent rounding. Otherwise $10.0129 and $10.0121 would be equal but their sum would be $20.025 rather than $20.024 ... accountants like things that "foot".
I support a financial .net application. There is a lot of advice to use the decimal datatype for financial stuff.
Now I am stuck with this:
decimal price = 1.0m/12.0m;
decimal quantity = 2637.18m;
decimal result = price * quantity; //results in 219.76499999999999999999999991
The problem is that the correct value to charge our customer is 219.77 (round function, MidpointRounding.AwayFromZero) and not 219.76.
If I change everything to double, it seems to work:
double price = 1.0/12.0;
double quantity = 2637.18;
double result = price * quantity; //results in 219.765
Shall I change everything to double? Will there be other problems with fractions?
I think this question is different from Difference between decimal, float and double in .NET? because it does not really explain to me why the result with a more precise datatype decimal is less accurate (in the sample above) than the result with the double datatype which uses fewer bytes.
The reason decimal is recommended is that all numbers that can be represented as non-repeating decimals can be accurately represented in a decimal type. Units of money in the real world are always non-repeating decimals. Your problem as others have said is that your price is, for some reason, not representable as a non-repeating decimal. That is it is 0.083333333.... Using a double doesn't actually help in terms of accuracy - a double can not accurately represent 1/12 either. In this case the lack of accuracy is not causing a problem but in others it might.
Also more importantly the choice to use a double will mean there are many more numbers that you couldn't represent completely accurately. For example 0.01, 0.02, 0.03... Yeah, quite a lot of numbers you are likely to care about can't be accurately represented as a double.
In this case the question of where the price comes from is really the important one. Wherever you are storing that price almost certainly isn't storing 1/12 exactly. Either you are storing an approximation already or that price is actually the result of a calculations (or you are using a very unusual number storage system where you are storing rational numbers but this seems wildly unlikely).
What you really want is a price that can be represented as a double. If that is what you have but then you modify it (eg by dividing by 12 to get a monthly cost from an annual) then you need to do that division as late as possible. And quite possibly you also need to calculate the monthly cost as a division of the outstanding balance. What I mean by this last part is that if you are paying $10 a year in monthly instalments you might charge $0.83 for the first month. Then the second month you charge ($10-0.83)/11. This would be 0.83 again. On the fifth month you charge (10-0.83*4)/8 which now is 0.84 (once rounded). Then next month its (10-0.83*4-0.84)/7 and so on. This way you guarantee that the total charge is correct and don't worry about compounded errors.
At the end of the day you are the only one to judge whether you can re-architect your system to remove all rounding errors like this or whether you have to mitigate them in some way as I've suggested. Your best bet though is to read up on everything you can about floating point numbers, both decimal and binary, so that you fully understand the implications of choosing one over the other.
Usually, in financial calculations, multiplications and divisions are expected to be rounded to a certain number of decimal places and in a certain way. (Most currency systems use only base-10 amounts of money; in these systems, non-base-10 amounts of money are rare, if they ever occur.) Dividing a price by 12 without more is not always expected to result in a base 10 number; the business logic will dictate how that price will be rounded, including the number of decimal places the result will have. Depending on the business logic, such a result as 0.083333333333333333 might not be the appropriate one.
I want my cake and to eat it. I want to beautify (round) numbers to the largest extent possible without compromising accuracy for other calculations. I'm using doubles in C# (with some string conversion manipulation too).
Here's the issue. I understand the inherent limitations in double number representation (so please don't explain that). HOWEVER, I want to round the number in some way to appear aesthetically pleasing to the end user (I am making a calculator). The problem is rounding by X significant digits works in one case, but not in the other, whilst rounding by decimal place works in the other, but not the first case.
Observe:
CASE A: Math.Sin(Math.Pi) = 0.000000000000000122460635382238
CASE B: 0.000000000000001/3 = 0.000000000000000333333333333333
For the first case, I want to round by DECIMAL PLACES. That would give me the nice neat zero I'm looking for. Rounding by Sig digits would mean I would keep the erroneous digits too.
However for the second case, I want to round by SIGNIFICANT DIGITS, as I would lose tons of accuracy if I rounded merely by decimal places.
Is there a general way I can cater to both types of calculation?
I don't thinks it's feasible to do that to the result itself and precision has nothing to do with it.
Consider this input: (1+3)/2^3 . You can "beautify" it by showing the result as sin(30) or cos(60) or 1/2 and a whole lot of other interpretations. Choosing the wrong "beautification" can mislead your user, making them think their function has something to do with sin(x).
If your calculator keeps all the initial input as variables you could keep all the operations postponed until you need the result and then make sure you simplify the result until it matches your needs. And you'll need to consider using rational numbers, e, Pi and other irrational numbers may not be as easy to deal with.
The best solution to this is to keep every bit you can get during calculations, and leave the display format up to the end user. The user should have some idea how many significant digits make sense in their situation, given both the nature of the calculations and the use of the result.
Default to a reasonable number of significant digits for a few calculations in the floating point format you are using internally - about 12 if you are using double. If the user changes the format, immediately redisplay in the new format.
The best solution is to use arbitrary-precision and/or symbolic arithmetic, although these result in much more complex code and slower speed. But since performance isn't important for a calculator (in case of a button calculator and not the one that you enter expressions to calculate) you can use them without issue
Anyway there's a good trade-off which is to use decimal floating point. You'll need to limit the input/output precision but use a higher precision for the internal representation so that you can discard values very close to zero like the sin case above. For better results you could detect some edge cases such as sine/cosine of 45 degree's multiples... and directly return the exact result.
Edit: just found a good solution but haven't had an opportunity to try.
Here’s something I bet you never think about, and for good reason: how are floating-point numbers rendered as text strings? This is a surprisingly tough problem, but it’s been regarded as essentially solved since about 1990.
Prior to Steele and White’s "How to print floating-point numbers accurately", implementations of printf and similar rendering functions did their best to render floating point numbers, but there was wide variation in how well they behaved. A number such as 1.3 might be rendered as 1.29999999, for instance, or if a number was put through a feedback loop of being written out and its written representation read back, each successive result could drift further and further away from the original.
...
In 2010, Florian Loitsch published a wonderful paper in PLDI, "Printing floating-point numbers quickly and accurately with integers", which represents the biggest step in this field in 20 years: he mostly figured out how to use machine integers to perform accurate rendering! Why do I say "mostly"? Because although Loitsch's "Grisu3" algorithm is very fast, it gives up on about 0.5% of numbers, in which case you have to fall back to Dragon4 or a derivative
Here be dragons: advances in problems you didn’t even know you had
I am wondering what's the best type for a price field in SQL Server for a shop-like structure?
Looking at this overview we have data types called money, smallmoney, then we have decimal/numeric and lastly float and real.
Name, memory/disk-usage and value ranges:
Money: 8 bytes (values: -922,337,203,685,477.5808 to +922,337,203,685,477.5807)
Smallmoney: 4 bytes (values: -214,748.3648 to +214,748.3647)
Decimal: 9 [default, min. 5] bytes (values: -10^38 +1 to 10^38 -1 )
Float: 8 bytes (values: -1.79E+308 to 1.79E+308 )
Real: 4 bytes (values: -3.40E+38 to 3.40E+38 )
Is it really wise to store price values in those types? What about eg. INT?
Int: 4 bytes (values: -2,147,483,648 to 2,147,483,647)
Lets say a shop uses dollars, they have cents, but I don't see prices being $49.2142342 so the use of a lot of decimals showing cents seems waste of SQL bandwidth. Secondly, most shops wouldn't show any prices near 200.000.000 (not in normal web-shops at least, unless someone is trying to sell me a famous tower in Paris)
So why not go for an int?
An int is fast, its only 4 bytes and you can easily make decimals, by saving values in cents instead of dollars and then divide when you present the values.
The other approach would be to use smallmoney which is 4 bytes too, but this will require the math part of the CPU to do the calc, where as Int is integer power... on the downside you will need to divide every single outcome.
Are there any "currency" related problems with regional settings when using smallmoney/money fields? what will these transfer too in C#/.NET ?
Any pros/cons? Go for integer prices or smallmoney or some other?
What does your experience tell?
If you're absolutely sure your numbers will always stay within the range of smallmoney, use that and you can save a few bytes. Otherwise, I would use money. But remember, storage is cheap these days. The extra 4 bytes over 100 million records is still less than half a GB. As #marc_s points out, however, using smallmoney if you can will reduce the memory footprint of SQL server.
Long story short, if you can get away with smallmoney, do. If you think you might go over the max, use money.
But, do not use a floating-decimal type or you will get rounding issues and will start losing or gaining random cents, unless you deal with them properly.
My argument against using int: Why reinvent the wheel by storing an int and then having to remember to divide by 100 (10000) to retrieve the value and multiply back when you go to store the value. My understanding is the money types use an int or long as the underlying storage type anyway.
As far as the corresponding data type in .NET, it will be decimal (which will also avoid rounding issues in your C# code).
Use the Money datatype if you are storing money (unless modelling huge amounts of money like the national debt) - it avoids precision/rounding issues.
The Many Benefits of Money…Data Type!
USE NUMERIC / DECIMAL. Avoid MONEY / SMALLMONEY. Here's an example of why. Sooner or later the MONEY / SMALLMONEY types will likely let you down due to rounding errors. The money types are completely redundant and achieve nothing useful - a currency amount being just another decimal number like any other.
Lastly, the MONEY / SMALLMONEY types are proprietary to Microsoft. NUMERIC / DECIMAL are part of the SQL standard. They are used, recognised and understood by more people and are supported by most DBMSs and other software.
Personally, I'd use smallmoney or money to store shop prices.
Using int adds complexity elsewhere.
And 200 million is perfectly valid price in Korean Won or Indonesian Rupees too...
SQL data types money and smallmoney both resolve to c# decimal type:
http://msdn.microsoft.com/en-us/library/system.data.sqltypes.sqlmoney(v=VS.71).aspx
So I'm thinking that you might as well go for decimal. Personally I've been using double all my life working in the financial industry and haven't experienced performance issues, etc. Actually, I've found that for certain calculations, etc., having a larger data type allows for higher degree of accuracy.
I would go for the Money datatype. Invididually you may not exceed the value in Smallmoney, but it would be easy for multiple items to exceed it.
In my pawnshop app, the pawnshop operators lend from $5.00 to $10,000.00
When they calculate the loan amount they round it to the nearest dollar in order to
avoid dealing with cents (the same applies for interest payments). When the loan amount is above $50.00 they will round it to the nearest $5.00 (i.e. $50, $55, $60 ...), again to minimize running out of dollar bills. Therefore, I use DECIMAL(7,2) for transaction.calculated_loan_amount and DECIMAL(5,0) for transaction.loan_amount.
The app calculates the loan amount to the penny and places that amount in loan_amount where it gets rounded to the nearest dollar when below $50 or to the nearest $5.00 when greater.
So, we know that fractions such as 0.1, cannot be accurately represented in binary base, which cause precise problems (such as mentioned here: Formatting doubles for output in C#).
And we know we have the decimal type for a decimal representation of numbers... but the problem is, a lot of Math methods, do not supporting decimal type, so we have convert them to double, which ruins the number again.
so what should we do?
Oh, what should we do about the fact that most decimal fractions cannot be represented in binary? or for that matter, that binary fractions cannot be represented in Decimal ?
or, even, that an infinity (in fact, a non-countable infinity) of real numbers in all bases cannot be accurately represented in any computerized system??
nothing! To recall an old cliche, You can get close enough for government work... In fact, you can get close enough for any work... There is no limit to the degree of accuracy the computer can generate, it just cannot be infinite, (which is what would be required for a number representation scheme to be able to represent every possible real number)
You see, for every number representation scheme you can design, in any computer, it can only represent a finite number of distinct different real numbers with 100.00 % accuracy. And between each adjacent pair of those numbers (those that can be represented with 100% accuracy), there will always be an infinity of other numbers that it cannot represent with 100% accuracy.
so what should we do?
We just keep on breathing. It really isn't a structural problem. We have a limited precision but usually more than enough. You just have to remember to format/round when presenting the numbers.
The problem in the following snippet is with the WriteLine(), not in the calculation(s):
double x = 6.9 - 10 * 0.69;
Console.WriteLine("x = {0}", x);
If you have a specific problem, th post it. There usually are ways to prevent loss of precision. If you really need >= 30 decimal digits, you need a special library.
Keep in mind that the precision you need, and the rounding rules required, will depend on your problem domain.
If you are writing software to control a nuclear reactor, or to model the first billionth of a second of the universe after the big bang (my friend actually did that), you will need much higher precision than if you are calculating sales tax (something I do for a living).
In the finance world, for example, there will be specific requirements on precision either implicitly or explicitly. Some US taxing jurisdictions specify tax rates to 5 digits after the decimal place. Your rounding scheme needs to allow for that much precision. When much of Western Europe converted to the Euro, there was a very specific approach to rounding that was written into law. During that transition period, it was essential to round exactly as required.
Know the rules of your domain, and test that your rounding scheme satisfies those rules.
I think everyone implying:
Inverting a sparse matrix? "There's an app for that", etc, etc
Numerical computation is one well-flogged horse. If you have a problem, it was probably put to pasture before 1970 or even much earlier, carried forward library by library or snippet by snippet into the future.
you could shift the decimal point so that the numbers are whole, then do 64 bit integer arithmetic, then shift it back. Then you would only have to worry about overflow problems.
And we know we have the decimal type
for a decimal representation of
numbers... but the problem is, a lot
of Math methods, do not supporting
decimal type, so we have convert them
to double, which ruins the number
again.
Several of the Math methods do support decimal: Abs, Ceiling, Floor, Max, Min, Round, Sign, and Truncate. What these functions have in common is that they return exact results. This is consistent with the purpose of decimal: To do exact arithmetic with base-10 numbers.
The trig and Exp/Log/Pow functions return approximate answers, so what would be the point of having overloads for an "exact" arithmetic type?