Does rounding floats generate a reliable result in C#? - c#

Given that I have two floats, a and b, and only care if they are "approximately equal", would something similar to the following work reliably, or would be it still be subject to precision issues?
eg:
Math.Round(a) == Math.Round(b)
Alternatively, is there a way to reliably round them to the nearest integer? If the above doesn't work, then I assume simply doing (int)Math.Round(a) won't be reliable either.
EDIT: I should have predicted I'd get answers like this, but I'm not trying to determine 'closeness' of the two values. Assuming the logic above is sound, will the above work, or is there a chance that I will get something like 3.0 == 3.0001?

Nothing of this kind will work. There is always a sharp boundary when very similar numbers will be rounded to different numbers.
In your first example think of a=0.4999999 and b=0.5000001. Very close but will round to different integers.
Another problem is that if the numbers are large rounding to an integer will have no effect at all. And even very close(relative) numbers will already an absolute difference >1.
If you care about determinism, you're out of luck with float and double. You just can't get those deterministic on .net. Use Decimal.

IF you really want it reliable then you will have to use Decimal instead of float... although that is much slower...
EDIT:
With ((int)Math.Round(a)) == ((int)Math.Round(b)) you avoid the problem 3.0 == 3.0001 BUT all the other pitfalls mentioned your post and in the answers will still apply...
You can complicate the logic to try to make it a bit more reliable (example see below which could be packaged nicely into some method) but it will never be really reliable...
// 1 = near, 2 = nearer, 3 = even nearer, 4 = nearest
int HowNear = 0;
if (((int)Math.Round(a)) == ((int)Math.Round(b)))
HowNear++;
if (((int)Math.Floor(a)) == ((int)Math.Floor(b)))
HowNear++;
if (((int)Math.Ceiling(a)) == ((int)Math.Ceiling(b)))
HowNear++;
if (Math.Round(a) == Math.Round(b))
HowNear++;

The correct way to convert from double/float to integers is:
(int)Math.Round(a)
having done this you will always get whole numbers which can be tested for equality. If you have numbers which you know are going to be virtually whole numbers (e.g. the result of 6.0/3.0) then this will work great. The recommended way of checking two numbers which are doubles/floats are approximately equal is:
Math.Abs(a-b)<tolerance
where tolerance is a double value that determines how similar they should be, for example, if you want them to be within 1 unit of each other you could use a tolerance of 1.0 which would give you similar accuracy to Math.Round and comparing the results, but is well behaved when you get two values which are very close to half way between two integers.

You could use Floor or Ceieling methods right to rounding of the values.

"approximately equal" is your answer actually, a pseudoexample:
double tollerance = 0.03;
if(Math.Abs(a-b)<=tollerance )
// these numbers are equal !
else
//non equal
EDIT
Or if you want to be more "precise":
int aint = (int)(a*100); // 100 is rounding tollerance
int bint = (int)(b*100); // 100 is rounding tollerance
and after
if(Math.Abs(aint -bint )<=tollerance ) // tollerance has to be integer in this case, obviously
// these numbers are equal !
else
//non equal

Related

Is this a good solution for R#'s complaint about loss of precision?

There was some code in my project that compared two double values to see if their difference exceeded 0, such as:
if (totValue != 1.0)
Resharper complained about this, suggesting I should use "EPSILON" and added just such a constant to the code (when invited to do so). However, it does not create the constant itself or suggest what value it should be. Is this a good solution:
const double EPSILON = double.Epsilon; // see http://msdn.microsoft.com/en-us/library/system.double.epsilon.aspx
. . .
if (Math.Abs(totValue - 1.0) > EPSILON)
compValue = Convert.ToString(totValue*Convert.ToDouble(compValue));
?
UPDATE
I changed it to this:
const double EPSILON = 0.001;
...thinking that's probably both large and small enough to work for typical double vals (not scientific stuff, just "I have 2.5 of these," etc.)
No, it is not a sensible value for your epsilon. The code you have is no different than a straight equality check.
double.Epsilon is the smallest possible difference that there can be between any two doubles. There is no way for any two doubles to be closer to each other than by being double.Epsilon apart, so the only way for this check to be true is for them to be exactly equal.
As for what epsilon value you should actually use, that simply depends, which is why one isn't automatically generated for you. It all depends on what types of operations you're doing to the data (which affects the possible deviation from the "true value") along with how much precision actually care about in your application (and of course if the precision that you care about is greater than your margin of error, you have a problem). Your epsilon needs to be some precision value greater than (or equal to) the precision you need, while being less than the possible margin of error of all operations performed on either numeric value.
Yes. But even better is to not use floating point. Use decimal instad.
However, if, for some reason, you have to stick to double never compare directly, that means, never rely on e.g. a-b == 0 with (a and b being some double values which are meant to be equal).
Floating point arithmetic is fast, but not precise, and taken that into account, R# is correct.

Force a relatively small inaccuracy in floating point number

I need to add a very small value to a floating point value to make it insignificantly different so that it fails an equality test.
To avoid issues with precision, instead of adding a very small number, I have opted to add a relatively small number. Is this a good solution? or is there a reliable way to add an even smaller number?
matrix.m00 += matrix.m00 * 0.0000001f;
matrix.m11 += matrix.m11 * 0.0000001f;
matrix.m22 += matrix.m22 * 0.0000001f;
From reading I have found that the best solution is to use the next representable floating point number. Though in C# the process of doing this either a) requires unmanaged/unsafe code, or b) uses BitConverter which is too slow. So I figured that the above solution would work, but I would like to know if there are any gotchyas.
You can add an ulp to any double (depends on the double); that is the smallest number that you can add or subtract to it that will change its value.
Calculate the unit in the last place (ULP) for doubles
next higher/lower IEEE double precision number
Though, those posts all use BitConverter. I discovered a post that discusses how to add an ulp without unsafe code or BitConverter, though:
http://realtimemadness.blogspot.com/2012/06/nextafter-in-c-without-allocations-of.html
Sure there's a gotcha. If any of these values is 0, then you'll be adding exactly 0, i.e. not modifying the value at all.
Is there any reason why you couldn't use unsafe code to do this?
The minimum number you can add to a floating point number such that a different number is produced is a function of the original number, it's not some constant. Call this function Epsilon(x).
Epsilon(0), i.e. the minimum floating point number you can add to floating point 0 such that a distinguishable number is produced, can be found in the static value Double.Epsilon.
Even using a "large" epsilon like 1 will eventually fail, though. For example, this returns true in C#:
var big = 10000000000000000.0;
Console.WriteLine(big == (big + 1.0));
So unless you are sure that your input is in some fixed range of magnitude (e.g. all close to 0), you can't just fudge it with a single constant.

C# loss of precision when dividing doubles

I know this has been discussed time and time again, but I can't seem to get even the most simple example of a one-step division of doubles to result in the expected, unrounded outcome in C# - so I'm wondering if perhaps there's i.e. some compiler flag or something else strange I'm not thinking of. Consider this example:
double v1 = 0.7;
double v2 = 0.025;
double result = v1 / v2;
When I break after the last line and examine it in the VS debugger, the value of "result" is 27.999999999999996. I'm aware that I can resolve it by changing to "decimal," but that's not possible in the case of the surrounding program. Is it not strange that two low-precision doubles like this can't divide to the correct value of 28? Is the only solution really to Math.Round the result?
Is it not strange that two low-precision doubles like this can't divide to the correct value of 28?
No, not really. Neither 0.7 nor 0.025 can be exactly represented in the double type. The exact values involved are:
0.6999999999999999555910790149937383830547332763671875
0.025000000000000001387778780781445675529539585113525390625
Now are you surprised that the division doesn't give exactly 28? Garbage in, garbage out...
As you say, the right result to represent decimal numbers exactly is to use decimal. If the rest of your program is using the wrong type, that just means you need to work out which is higher: the cost of getting the wrong answer, or the cost of changing the whole program.
Precision is always a problem, in case you are dealing with float or double.
Its a known issue in Computer Science and every programming language is affected by it. To minimize these sort of errors, which are mostly related to rounding, a complete field of Numerical Analysis is dedicated to it.
For instance, let take the following code.
What would you expect?
You will expect the answer to be 1, but this is not the case, you will get 0.9999907.
float v = .001f;
float sum = 0;
for (int i = 0; i < 1000; i++ )
{
sum += v;
}
It has nothing to do with how 'simple' or 'small' the double numbers are. Strictly speaking, neither 0.7 or 0.025 may be stored as exactly those numbers in computer memory, so performing calculations on them may provide interesting results if you're after heavy precision.
So yes, use decimal or round.
To explain this by analogy:
Imagine that you are working in base 3. In base 3, 0.1 is (in decimal) 1/3, or 0.333333333'.
So you can EXACTLY represent 1/3 (decimal) in base 3, but you get rounding errors when trying to express it in decimal.
Well, you can get exactly the same thing with some decimal numbers: They can be exactly expressed in decimal, but they CAN'T be exactly expressed in binary; hence, you get rounding errors with them.
Short answer to your first question: No, it's not strange. Floating-point numbers are discrete approximations of the real numbers, which means that rounding errors will propagate and scale when you do arithmetic operations.
Theres' a whole field of mathematics called numerical analyis that basically deal with how to minimize the errors when working with such approximations.
It's the usual floating point imprecision. Not every number can be represented as a double, and those minor representation inaccuracies add up. It's also a reason why you should not compare doubles to exact numbers. I just tested it, and result.ToString() showed 28 (maybe some kind of rounding happens in double.ToString()?). result == 28 returned false though. And (int)result returned 27. So you'll just need to expect imprecisions like that.

Is it safe to compare two floored doubles using the equality operator?

I know you should never compare floating point value using the == equality operator in .NET, but is it safe to do so if the two numbers were floored using Math.Floor?
I am working with a mapping program, and chunks of the map are stored in different "region" files. I can determine what region to retrieve by dividing the world coordinates by 16 and flooring the result, which gets me region coordinates.
I'm essentially asking whether or not two values that have the same whole number portion (e.g. 4.3 and 4.8) that are floored will be compared as equal using the == operator.
The general issue with floating point comparisons is that they can easily accrue rounding error. When you take a value like 1.2 (which cannot be exactly represented as a decimal) multiply it by 100 and compare it for equality to 120. The recommendation is to always compare the difference like so:
var a = 1.2;
a *= 100;
if (a - 120 < 0.0001)
{
}
The Math.Floor operation, however, always results in an integer value. That is to say that any fractional values will be truncated, and the exact integer value will be left.
So, if your semantics really are to use a floor, you are safe.
However, if you are really trying to round, then use Math.Round() instead.
Well, it depends on what you're trying to do.
That will tell you whether the floored values are equal - but if one input was just a smidge under 2, and one input was just a smidge over 2, then they'll be seen as different, despite the difference between them being potentially tiny.
Is that okay for your scenario? In some cases it will be, in some it won't.
I think your question is predicated on a faulty assumption. It's perfectly safe to compare floating point values using == in .Net. The only odd behavior associated with == and floating point values is that Double.NaN and Single.NaN when compared to themselves with == will return false (as dictated by the floating point specification).
Using Math.Floor doesn't make this situation any better. If any of the special floating point values (NaN, NegativeInfinity, PositiveInfinity) are passed to Math.Floor they are returned unaltered. So the comparison via == will still have the odd behavior (Reference)
The main effect using Math.Floor will have is more floating values will compare equal to each other. For example 7.1 and 7.5 will be equal after a Math.Floor. That's not inherently any better but could be in the context of your application but it's hard to say it will be without more information.. Could you provide some more detail here on why you think == is unsafe?

Why can't c# calculate exact values of mathematical functions

Why can't c# do any exact operations.
Math.Pow(Math.Sqrt(2.0),2) == 2.0000000000000004
I know how doubles work, I know where the rounding error is from, I know that it's almost the correct value, and I know that you can't store infinite numbers in a finite double. But why isn't there a way that c# can calculate it exactly, while my calculator can do it.
Edit
It's not about my calculator, I was just giving an example:
http://www.wolframalpha.com/input/?i=Sqrt%282.000000000000000000000000000000000000000000000000000000000000000000000000000000001%29%5E2
Cheers
Chances are your calculator can't do it exactly - but it's probably storing more information than it's displaying, so the error after squaring ends up outside the bounds of what's displayed. Either that, or its errors happen to cancel out in this case - but that's not the same as getting it exactly right in a deliberate way.
Another option is that the calculator is remembering the operations that resulted in the previous results, and applying algebra to cancel out the operations... that seems pretty unlikely though. .NET certainly won't try to do that - it will calculate the intermediate value (the root of two) and then square it.
If you think you can do any better, I suggest you try writing out the square root of two to (say) 50 decimal places, and then square it exactly. See whether you come out with exactly 2...
Your calculator is not calculating it exactly, it just that the rounding error is so small that it's not displayed.
I believe most calculators use binary-coded decimals, which is the equivalent of C#'s decimal type (and thus is entirely accurate). That is, each byte contains two digits of the number and maths is done via logarithms.
What makes you think your calculator can do it? It's almost certainly displaying less digits than it calculates with and you'd get the 'correct' result if you printed out your 2.0000000000000004 with only five fractional digits (for example).
I think you'll probably find that it can't. When I do the square root of 2 and then multiply that by itself, I get 1.999999998.
The square root of 2 is one of those annoying irrational numbers like PI and therefore can't be represented with normal IEEE754 doubles or even decimal types. To represent it exactly, you need a system capable of symbolic math where the value is stored as "the square root of two" so that subsequent calculations can deliver correct results.
The way calculators round up numbers vary from model to model. My TI Voyage 200 does algebra to simplify equations (among other things) but most calculators will display only a portion of the real value calculated, after applying a round function on the result. For example, you may find the square root of 2 and the calculator would store (let's say) 54 decimals, but will only display 12 rounded decimals. Thus when doing a square root of 2, then do a power of that result by 2 would return the same value since the result is rounded. In any case, unless the calculator can keep an infinite number of decimals, you'll always have a best approximate result from complexe operations.
By the way, try to represent 10.0 in binary and you'll realize that you can't represent it evenly and you'll end up with (something like) 10.00000000000..01
Your calculator has methods which recognize and manipulate irrational input values.
For example: 2^(1/2) is likely not evaluated to a number in the calculator if you do not explicitly tell it to do so (as in the ti89/92).
Additionally, the calculator has logic it can use to manipulate them such as x^(1/2) * y^(1/2) = (x*y)^1/2 where it can then wash, rinse, repeat the method for working with irrational values.
If you were to give c# some method to do this, I suppose it could as well. After all, algebraic solvers such as mathematica are not magical.
It has been mentioned before, but I think what you are looking for is a computer algebra system. Examples of these are Maxima and Mathematica, and they are designed solely to provide exact values to mathematical calculations, something not covered by the CPU.
The mathematical routines in languages like C# are designed for numerical calculations: it is expected that if you are doing calculations as a program you will have simplified it already, or you will only need a numerical result.
2.0000000000000004 and 2. are both represented as 10. in single precision. In your case, using single precision for C# should give the exact answer
For your other example, Wolfram Alpha may use higher precision than machine precision for calculation. This adds a big performance penalty. For instance, in Mathematica, going to higher precision makes calculations about 300 times slower
k = 1000000;
vec1 = RandomReal[1, k];
vec2 = SetPrecision[vec1, 20];
AbsoluteTiming[vec1^2;]
AbsoluteTiming[vec2^2;]
It's 0.01 second vs 3 seconds on my machine
You can see the difference in results using single precision and double precision introduced by doing something like the following in Java
public class Bits {
public static void main(String[] args) {
double a1=2.0;
float a2=(float)2.0;
double b1=Math.pow(Math.sqrt(a1),2);
float b2=(float)Math.pow(Math.sqrt(a2),2);
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(a1)));
System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(a2)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(b1)));
System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(b2)));
}
}
You can see that single precision result is exact, whereas double precision is off by one bit

Categories