Using Entity Framework 6, Code first approach
When setting the precision of a property using the HasPrecision method in an EntityTypeConfiguration class, it seems only to affect the truncate behavior of the decimal value. Is there any way to force Entity Framework to throw an error in case the precision is not as specified (same as MaxLength does if the string length is too long).
Example:
this.Property(t => t.Amount).HasColumnName("Amount").IsRequired().HasPrecision(19, 2);
When setting Amount to 5.4567 and saving the entity, the value being saved is 5.45
I would prefer to get an error in such a case.
What would be the best way to achieve it?
I think a way could be to create your validation logic using
DbContext.ValidateEntity method
more info here
In short: no, there's no such way. That's how non-integer numbers work in a computer. Decimal numbers in a computer are generally not exact, and they only get exact by rounding.
Such is that, that if you do:
Console.WriteLine((0.1 + 0.2).ToString("R"));
The output will be 0.30000000000000004 (the R there is so it can show the 17 digits that double stores, instead of the 15 it shows by default on a ToString()). You can check it here.
You only get 0.3 as a result if you actually round it, but the internal storage is not exact, and it's only an approximation of 0.3 (so approximate that if you round to any significant amount of digits -any less than 17 decimal positions in this case-, you'll get 0.3).
This is not arbitrary, it's defined in the IEEE 754 standard (wikipedia).
So there's just no way (except for some carefully chosen numbers) you could get to store exactly-rounded-to n-th precision numbers in a computer and it'd be throwing constantly.
You could validate your entries if you want to throw, but I just can't think of a scenario in which doing that is practical.
If you go the way of validating, please notice that:
Console.WriteLine((0.1+0.2)==0.3);
Returns False (for precision issues), so be wary of HOW you validate your entries to be rounded
Related
The documentation recommends that I use G17 rather than R, as R can sometimes fail to round-trip.
However, (1.0/10).ToString("G17") gives "0.10000000000000001", which is pretty horrible. while the round-trip format seems to work just fine (and gives "0.1"). I'm happy to spend a few cpu cycles in order to get a more aesthetic result. But potential round-trip failures are more concerning.
For what sort of (Double) values does R fail to round-trip? And how badly?
Does the .Net version (we run on both Net Framework 4.72 and NetCore 3.1) affect things? Could writing on one platform and reading on another make round-trip failure more frequent?
We are considering writing doubles to R first, parsing to check the round-trip, and falling back to G17 only if that fails. Is there a better way to get nicely formatted, reliable results?
Round trip here is a numeric value that is converted to a string is parsed back into the same numeric value.
OP's dismay with (1.0/10).ToString("G17") gives "0.10000000000000001", which is pretty horrible. is an incorrect assessment of round trip success. The intermediate string is only half of the round trip.
Double exactly encodes about 264 different values. All encodable values are some limited integer * 2some_power. 0.1 is not one of them. 1.0/10 makes a math quotient of 0.1, but a slightly different Double value. The closest Double value and it two closet Double neighbors:
Before 0.099999999999999991673...
0.100000000000000005551...
After 0.100000000000000019428...
OP report 0.10000000000000001
Digit count 12345678901234567
OP's example should then be <(0.100000000000000005551).ToString("G17") gives "0.10000000000000001"> which is good.
Printing a Double with G17 provides 17 significant digits, enough to successfully round trip.
For what sort of (Double) values does R fail to round-trip? And how badly?
For this, I go on memory. R sometimes used less than 17 significant digits, like 15, to form the intermediate string. The algorithm used to determine the digit count sometimes came up a bit short and hence "some cases fails to successfully round-trip the original value".
Using G17 always works. For some values, less than 17 also would have worked. The down-side to G17 is exactly in cases like this. Fewer than 17 digits would have worked and have provided a more pleasing, shorter intermediate string.
A pleasing human readable string is not the goal of round-tripping. The goal is to form the same Double after going form value to string to value, even if the intermediate string has extra digits in select cases.
Is there a better way to get nicely formatted, reliable results?
"nicely formatted" is an additional burden to round trip. MS attempted to do so with R and failed in some cases, preferring to retain the same broken functionality than to fix it.
OP would be wise to avoid that path and forego the goal of nicely formatted intermediate string and focus on the round-trip goal of getting the final same value back.
Use G17.
We are considering writing doubles to R first, parsing to check the round-trip, and falling back to G17 only if that fails.
That would work if done correctly. To assess correctness, test your code with many values and also post it and the test harness for code review.
This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 7 years ago.
If I execute the following expression in C#:
double i = 10*0.69;
i is: 6.8999999999999995. Why?
I understand numbers such as 1/3 can be hard to represent in binary as it has infinite recurring decimal places but this is not the case for 0.69. And 0.69 can easily be represented in binary, one binary number for 69 and another to denote the position of the decimal place.
How do I work around this? Use the decimal type?
Because you've misunderstood floating point arithmetic and how data is stored.
In fact, your code isn't actually performing any arithmetic at execution time in this particular case - the compiler will have done it, then saved a constant in the generated executable. However, it can't store an exact value of 6.9, because that value cannot be precisely represented in floating point point format, just like 1/3 can't be precisely stored in a finite decimal representation.
See if this article helps you.
why doesn't the framework work around this and hide this problem from me and give me the
right answer,0.69!!!
Stop behaving like a dilbert manager, and accept that computers, though cool and awesome, have limits. In your specific case, it doesn't just "hide" the problem, because you have specifically told it not to. The language (the computer) provides alternatives to the format, that you didn't choose. You chose double, which has certain advantages over decimal, and certain downsides. Now, knowing the answer, you're upset that the downsides don't magically disappear.
As a programmer, you are responsible for hiding this downside from managers, and there are many ways to do that. However, the makers of C# have a responsibility to make floating point work correctly, and correct floating point will occasionally result in incorrect math.
So will every other number storage method, as we do not have infinite bits. Our job as programmers is to work with limited resources to make cool things happen. They got you 90% of the way there, just get the torch home.
And 0.69 can easily be represented in
binary, one binary number for 69 and
another to denote the position of the
decimal place.
I think this is a common mistake - you're thinking of floating point numbers as if they are base-10 (i.e decimal - hence my emphasis).
So - you're thinking that there are two whole-number parts to this double: 69 and divide by 100 to get the decimal place to move - which could also be expressed as:
69 x 10 to the power of -2.
However floats store the 'position of the point' as base-2.
Your float actually gets stored as:
68999999999999995 x 2 to the power of some big negative number
This isn't as much of a problem once you're used to it - most people know and expect that 1/3 can't be expressed accurately as a decimal or percentage. It's just that the fractions that can't be expressed in base-2 are different.
but why doesn't the framework work around this and hide this problem from me and give me the right answer,0.69!!!
Because you told it to use binary floating point, and the solution is to use decimal floating point, so you are suggesting that the framework should disregard the type you specified and use decimal instead, which is very much slower because it is not directly implemented in hardware.
A more efficient solution is to not output the full value of the representation and explicitly specify the accuracy required by your output. If you format the output to two decimal places, you will see the result you expect. However if this is a financial application decimal is precisely what you should use - you've seen Superman III (and Office Space) haven't you ;)
Note that it is all a finite approximation of an infinite range, it is merely that decimal and double use a different set of approximations. The advantage of decimal is it produces the same approximations that you would if you were performing the calculation yourself. For example if you calculated 1/3, you would eventually stop writing 3's when it was 'good enough'.
For the same reason that 1 / 3 in a decimal systems comes out as 0.3333333333333333333333333333333333333333333 and not the exact fraction, which is infinitely long.
To work around it (e.g. to display on screen) try this:
double i = (double) Decimal.Multiply(10, (Decimal) 0.69);
Everyone seems to have answered your first question, but ignored the second part.
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'm learning TDD and, decided to create a Calculator class to start.
i did the basic first, and now I'm on the Square Root function.
I'm using this method to get the root http://www.math.com/school/subject1/lessons/S1U1L9DP.html
i tested it with few numbers, and I always get the accurate answers.
is pretty easy to understand.
Now I'm having a weird problem, because with some numbers, im getting the right answer, and with some, I don't.
I debugged the code, and found out that I'm not getting the right answer when I use subtract.
I'm using decimals to get the most accurate result.
when I do:
18 / 4.25
I am currently getting: 4.2352941176470588235294117647
when it should be: 4.2352941176470588235294117647059 (using windows calculator)
in the end of the road, this is the closest i get to the root of 18:
4.2426406871192851464050688705 ^ 2 = 18.000000000000000000000022892
my question is:
Can i get more accurate then this?
4.2352941176470588235294117647 contains 29 digits.
decimal is define to have 28-29 significant digits. You can't store a more accurate number in a decimal.
What field of engineering or science are you working in where the 30th and more digits are significant to the accuracy of the overall calculation?
(It would also, possibly, help if you'd shown some more actual code. The only code you've shown is 18 / 4.25, which can't be an actual expression in your code, since the second number is a double literal, and you can't assign the result of this expression to a decimal without a cast).
If you need arbitrary precision, then there isn't a standard "BigRational" type, but there is a BigInteger. You could use that to construct a BigRational type if you need that (storing numerator and denominator as two separate integers). One guess of why there isn't a standard type yet is that decisions on when to e.g. normalize such rationals may affect performance or equality comparisons.
Floating point calculations are not accurate. Decimals make the accuracy better, because they are 128-bit long, but they are still floating point numbers.
Comparing two floating point numbers is not done with ==, but rather:
static bool SameDecimal(decimal a, decimal b)
{
return Math.Abs(a-b) < 1e-10;
}
This method will allow you to compare two decimals (I assume 1e-10 is a small enough difference for you, it should be for everyday uses).
I made a query to SQL Server to get some data via a Stored Procedure, the returned value was this:
10219150
Then, in an assembly (I don't have the source code of that assembly, I reflected the file to view the code) someone had written this:
Amount = Convert.ToSingle(10219150); //the value from the stored procedure
So, when I invoke that method which does the final conversion, it returns this value:
1.021315E+7
How is that possible? Why does the Convert.ToSingle add extra decimal positions? I don't understand.
Is there a way that i can reverse that conversion on my code when I invoke that method of the assembly? I can't rewrite that assembly file as it's too big, and, as I mentioned earlier, I don't have the source code to fix the conversion.
From this: 1.021315E+7 To this: 10219150 again (restore the correct value without that conversion)
Hope I made myself clear.
Thanks in advance.
The conversion to single isn't adding extra precision.
10219150 is 1.021315E+7 (which is just another way of writing 1.021315 * 107).
The method you are using to print out the value is just using scientific notation to display the number.
If you are printing the number then you need to set the formatting options.
float amount = Convert.ToSingle("10219150");
string toPrint = string.Format("{0:N}", amount);
Will print the number as:
"10,219,150.00"
To get no decimal places use "{0:N0}" as the format string.
You have two issues. One is easily solved, and the other may be more difficult or impossible.
As ChrisF stated, 1.021315E+7 is simply another way of writing 10219150. (The E+7 part in Scientific Notation means to shift the decimal point 7 places to the right.) When you format your single precision value, you can use
fvalue.ToString("f0");
to display as an integer, rather than in Scientific Notation.
The bigger problem, unfortunately, is that a single precision float can only hold 7 significant digits, and in your example you are storing 8. Therefore, the last digit may be rounded. (Since it happens to be 0 in your case, the rounding might not have been noticed.)
If that loss of precision is critical, you would likely need to fetch the value from the database as a long, or as a double-precision value (depending on the type of data returned.) Each of these types can hold more significant digits.
When the value is converted to Single, it's rounded as it contains more significant digits that can fit in a Single. If you convert 10213153 to Single you also end up with 1.021315E+7 i.e. 10213150.
As the code uses a Single to store the amount, there is nothing that you can do to make it handle the current value correctly. The amount simply can not be represented correctly as a Single.
You either have to use lower values, or change the code.