Decimal out of range exception - c#

In my C# application, I am trying to save a decimal price into a SQL Server table. Columns type is decimal with no total digits defined.
Without discount calculations, everything works fine.
But when I run some calculations, I get a final value of 21800, and I get the following error when trying to save it.
"Parameter value '218000/00000000000000' is out of range."
I don't understand where the extra zeros come from! The only thing I know is with myValue.ToString() I get 218000/00000000000000, too!
I understand that the digits after floating point are caused by the calculations. But whatever they do, my value is 21800 when watching it. Why is it trying to save 218000/00000000000000? Why does it care at all about the zeros?
Any ideas why this happens and how to fix it?

There are two ways to store decimal numbers:
Fixed-Point: This is what you have defined in your SQL Server project. You defined decimal (19,4), which means 19 digits total and 4 digits after the decimal point. If you define such a type, then every number always has 4 digits after the decimal point; and also, you cannot store a number with more than 4 digits after the decimal point. And note, there is no equivalent to fixed point in C#!
Floating Point: These are the types float, double and decimal in C#, and the type float(n)in SQL Server. In floating point, as the names says, you can basically vary the number of digits before and behind the decimal point. (Technically, you have a fixed number of digits, the so-called mantissa, and you can then move the decimal point around with a 2nd number, the exponent)
So now to your project: In your database, you use fixed point, while in C#, you have floating point. Therefore, when you calculate something in C#, and you want to store in to your database, then it is always converted from floating point to fixed point. However, if your number in C# does not fit the decimal numbers in the database, then you get the out of range error.

I had to specify the total digits of my decimal column in the SQL table (I set it as decimal (19, 4)).
Besides, my C# representing field also needed to have the right precision count.
I still don't know about the extra zeros and why SQL care about them. Any explanations is appreciated.

Related

Convert a number string to a rounded decimal (C#)

Sorry for the daft question, but I get back this value from database
"7.545720553985866E+29"
I need to convert this value to a decimal, rounded to 6 digits. What is the best way to do that? I tried
var test = double.Parse("7.545720553985866E+29");
test = Math.Round(test, 6);
var test2 = Convert.ToDecimal(test);
but the value remains unchanged and the conversion crashes.
Math.Round rounds to N digits to the right of the decimal point. Your number has NO digits to the right of the decimal (it is equivalent to 754,572,055,398,586,600,000,000,000,000), so rounding it does not change the value.
If you want to round to N significant digits then look at some of the existing answers:
Round a double to x significant figures
Rounding the SIGNIFICANT digits in a double, not to decimal places
the conversion crashes.
That's because the value is too large for a decimal. The largest value a decimal can hold is 7.9228E+28 - your value is about 10 times larger than that.
Maybe you can substring it and then after, parse.
var test= "7.545720553985866E+29".Substring(0,8); // 7.545720
test = Math.Round(test, 6);
var test2 = Convert.ToDecimal(test);
You can use this to round to 6 significant digits:
round(test, 6 - int(math.log10(test)))
The resulting value from that is
7.545721e+29
This works by using log10 from the math module to get the power of 10 in test, rounds it down to get an integer, subtracts that from 6 then uses round to get the desired digits.
As noted by others, round works to the given number of decimal places. The log10 and the rest figures how many decimal places are needed to get the desired number of significant digits. If the decimal places are negative, round rounds to the left of the decimal point.
You should be aware that log10 is not perfectly accurate and taking the int of that may be off from the expected value by one. This happens rarely but it does happen. Also, even if the computed value is correct, converting the value to string (such as when you print it) may give a different-than-expected result. If you need perfect accuracy you would be better off working from the string representation of the value.

What MySQL "DECIMAL" precision and scale is required to store a C# decimal value?

I want to store some decimal values in my MySQL database. The obvious MySQL database type for that would be DECIMAL I guess.
What precision "a" and scale "b" do I have to set for DECIMAL(a,b ) exactly, to be able to represent/store the complete range of the C# decimal value type?
In a DECIMAL column declaration, the precision and scale can be (and usually is) specified; for example:
salary DECIMAL(5,2)
In this example, 5 is the precision and 2 is the scale. The precision represents the number of significant digits that are stored for values, and the scale represents the number of digits that can be stored following the decimal point.
From what I understand decimal can have up to 29 significant digits. The position of the decimal point depends on the actual value, or am I making a mistake here? That would mean I can have a worst case of either 29 digits before or 29 digits after the decimal separator.
So I would I assume , that I would have to use at least something like DECIMAL(58,29) to handle both cases in the database?

C# Convert.ToDouble() loses decimal points when converting string to double

Let's say we have the following simple code
string number = "93389.429999999993";
double numberAsDouble = Convert.ToDouble(number);
Console.WriteLine(numberAsDouble);
after that conversion numberAsDouble variable has the value 93389.43. What can i do to make this variable keep the full number as is without rounding it? I have found that Convert.ToDecimal does not behave the same way but i need to have the value as double.
-------------------small update---------------------
putting a breakpoint in line 2 of the above code shows that the numberAsDouble variable has the rounded value 93389.43 before displayed in the console.
93389.429999999993 cannot be represented exactly as a 64-bit floating point number. A double can only hold 15 or 16 digits, while you have 17 digits. If you need that level of precision use a decimal instead.
(I know you say you need it as a double, but if you could explain why, there may be alternate solutions)
This is expected behavior.
A double can't represent every number exactly. This has nothing to do with the string conversion.
You can check it yourself:
Console.WriteLine(93389.429999999993);
This will print 93389.43.
The following also shows this:
Console.WriteLine(93389.429999999993 == 93389.43);
This prints True.
Keep in mind that there are two conversions going on here. First you're converting the string to a double, and then you're converting that double back into a string to display it.
You also need to consider that a double doesn't have infinite precision; depending on the string, some data may be lost due to the fact that a double doesn't have the capacity to store it.
When converting to a double it's not going to "round" any more than it has to. It will create the double that is closest to the number provided, given the capabilities of a double. When converting that double to a string it's much more likely that some information isn't kept.
See the following (in particular the first part of Michael Borgwardt's answer):
decimal vs double! - Which one should I use and when?
A double will not always keep the precision depending on the number you are trying to convert
If you need to be precise you will need to use decimal
This is a limit on the precision that a double can store. You can see this yourself by trying to convert 3389.429999999993 instead.
The double type has a finite precision of 64 bits, so a rounding error occurs when the real number is stored in the numberAsDouble variable.
A solution that would work for your example is to use the decimal type instead, which has 128 bit precision. However, the same problem arises with a smaller difference.
For arbitrary large numbers, the System.Numerics.BigInteger object from the .NET Framework 4.0 supports arbitrary precision for integers. However you will need a 3rd party library to use arbitrary large real numbers.
You could truncate the decimal places to the amount of digits you need, not exceeding double precision.
For instance, this will truncate to 5 decimal places, getting 93389.42999. Just replace 100000 for the needed value
string number = "93389.429999999993";
decimal numberAsDecimal = Convert.ToDecimal(number);
var numberAsDouble = ((double)((long)(numberAsDecimal * 100000.0m))) / 100000.0;

How to decide what to use - double or decimal? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
decimal vs double! - Which one should I use and when?
I'm using double type for price in my trading software.
I've noticed that sometimes there are a odd errors.
They occur if price contains 4 digits after "dot", like 2.1234.
When I sent from my program "2.1234" on the market order appears at the price of "2.1235".
I don't use decimal because I don't need "extreme" precision. I don't need to distinguish for examle "2.00000000003" from "2.00000000002". I need maximum 6 digits after a dot.
The question is - where is the line? When to use decimal?
Should I use decimal for any finansical operations? Even if I need just one digit after the dot? (1.1 1.2 etc.)
I know decimal is pretty slow so I would prefer to use double unless decimal is absolutely required.
Use decimal whenever you're dealing with quantities that you want to (and can) be represented exactly in base-10. That includes monetary values, because you want 2.1234 to be represented exactly as 2.1234.
Use double when you don't need an exact representation in base-10. This is usually good for handling measurements, because those are already approximations, not exact quantities.
Of course, if having or not an exact representation in base-10 is not important to you, other factors come into consideration, which may or may not matter depending on the specific situation:
double has a larger range (it can handle very large and very small magnitudes);
decimal has more precision (has more significant digits);
you may need to use double to interact with some older APIs that are not aware of decimal;
double is faster than decimal;
decimal has a larger memory footprint;
When accuracy is needed and important, use decimal.
When accuracy is not that important, then you can use double.
In your case, you should be using decimal, as its financial matter.
For financial operation I always use the decimal type
Use decimal it's built for representing powers of 10 well (i.e. prices).
Decimal is the way to go when dealing with prices.
If it's financial software you should probably use decimal. This wiki article summarises quite nicely.
A simple response is in this example:
decimal d = 0.3M+0.3M+0.3M;
bool ret = d == 0.9M; // true
double db = 0.3 + 0.3 + 0.3;
bool dret = db == 0.9; // false
the test with the double fails since 0.3 in its binary representation ( base 2 ) is periodic, so you loose precision the decimal is represented by BCD, so base 10, and you did not loose significant digit unexpectedly. The Decimal are unfortunately dramattically slower than double. Usually we use decimal for financial calculation, where any digit has to be considered to avoid tolerance, double/float for engineering.
Double is meant as a generic floating-point data type, decimal is specifically meant for money and financial domains. Even though double usually works just fine decimal might prevent problems in some cases (e.g. rounding errors when you get to values in the billions)
There is an Explantion of it on MSDN
As soon as you start to do calculations on doubles you may get unexpected rounding problems because a double uses a binary representation of the number while the decimal uses a decimal representation preserving the decimal digits. That is probably what you are experiencing. If you only serialize and deserialize doubles to text or database without doing any rounding you will actually not loose any precision.
However, decimals are much more suited for representing monetary values where you are concerned about the decimal digits (and not the binary digits that a double uses internally). But if you need to do complex calculations (e.g. integrals as used by actuary computations) you will have to convert the decimal to double before doing the calculation negating the advantages of using decimals.
A decimal also "remembers" how many digits it has, e.g. even though decimal 1.230 is equal to 1.23 the first is still aware of the trailing zero and can display it if formatted as text.
If you always know the maximum amount of decimals you are going to have (digits after the point). Then the best practice is to use fixed point notation. That will give you an exact result while still working very fast.
The simplest manner in which to use fixed point is to simply store the number in an int of thousand parts. For example if the price always have 2 decimals you would be saving the amount of cents ($12.45 is stored in an int with value 1245 which thus would represent 1245 cents). With four decimals you would be storing pieces of ten-thousands (12.3456 would be stored in an int with value 123456 representing 123456 ten-thousandths) etc etc.
The disadvantage of this is that you would sometimes need a conversion if for example you are multiplying two values together (0.1 * 0.1 = 0.01 while 1 * 1 = 1, the unit has changed from tenths to hundredths). And if you are going to use some other mathematical functions you also has to take things like this into consideration.
On the other hand if the amount of decimals vary a lot using fixed point is a bad idea. And if high-precision floating point calculations are needed the decimal datatype was constructed for exactly that purpose.

ODP ADO.NET driver returns decimal with extra zero in string representation

Today's problem is as follows: we have a table in an Oracle database. The table contains a field that is of the type Number(18, 3).
On the surface, both saving and loading data from said field work perfectly. However, further inspection reveals, that numbers that have three decimal digits in them, e.g. 500.001, are read from the database in such a way that the string representation of the decimal has a fourth, zero digit (e.g. 500.0010). This seems to happen whenever the third decimal digit is nonzero.
For math purposes, this is not a problem. However, we have validators that verify the number of decimal digits in a decimal number by converting it to a string and then counting the number of decimal digits. Since this particular piece of data is defined to have at most three decimal digits, the extra zero causes a validation error.
Since I can't change the validators, I'm left wondering if there is a clean way to get the decimal without the extra zero, apart from converting the number to a string, trimming the trailing zeroes and then re-parsing it? Or should I be doing something different altogether?
Try this:
decimal trimmed = ((decimal)((int)(number * 1000))) / 1000
Casting to int gets rid of the fractional part. You don't need all the parentheses but I think it's easier to see what's going on if the order of operations is explictily indicated.
I ran into the same issue but I used: number.ToString("F3")

Categories

Resources