Double precision problems on .NET - c#

I have a simple C# function:
public static double Floor(double value, double step)
{
return Math.Floor(value / step) * step;
}
That calculates the higher number, lower than or equal to "value", that is multiple of "step". But it lacks precision, as seen in the following tests:
[TestMethod()]
public void FloorTest()
{
int decimals = 6;
double value = 5F;
double step = 2F;
double expected = 4F;
double actual = Class.Floor(value, step);
Assert.AreEqual(expected, actual);
value = -11.5F;
step = 1.1F;
expected = -12.1F;
actual = Class.Floor(value, step);
Assert.AreEqual(Math.Round(expected, decimals),Math.Round(actual, decimals));
Assert.AreEqual(expected, actual);
}
The first and second asserts are ok, but the third fails, because the result is only equal until the 6th decimal place. Why is that? Is there any way to correct this?
Update If I debug the test I see that the values are equal until the 8th decimal place instead of the 6th, maybe because Math.Round introduces some imprecision.
Note In my test code I wrote the "F" suffix (explicit float constant) where I meant "D" (double), so if I change that I can have more precision.

I actually sort of wish they hadn't implemented the == operator for floats and doubles. It's almost always the wrong thing to do to ever ask if a double or a float is equal to any other value.

If you want precision, use System.Decimal. If you want speed, use System.Double (or System.Float). Floating point numbers are not "infinite precision" numbers, and therefore asserting equality must include a tolerance. As long as your numbers have a reasonable number of significant digits, this is ok.
If you're looking to do math on very large AND very small numbers, don't use float or double.
If you need infinite precision, don't use float or double.
If you are aggregating a very large number of values, don't use float or double (the errors will compound themselves).
If you need speed and size, use float or double.
See this answer (also by me) for a detailed analysis of how precision affects the outcome of your mathematical operations.

Floating point arithmetic on computers are not Exact Science :).
If you want exact precision to a predefined number of decimals use Decimal instead of double or accept a minor interval.

If you omit all the F postfixes (ie -12.1 instead of -12.1F) you will get equality to a few digits more. Your constants (and especially the expected values) are now floats because of the F. If you are doing that on purpose then please explain.
But for the rest i concur with the other answers on comparing double or float values for equality, it's just not reliable.

http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
For example, the non-representability of 0.1 and 0.01 (in binary) means that the result of attempting to square 0.1 is neither 0.01 nor the representable number closest to it.
Only use floating point if you want a machine's interpretation (binary) of number systems. You can't represent 10 cents.

Check the answers to this question: Is it safe to check floating point values for equality to 0?
Really, just check for "within tolerance of..."

floats and doubles cannot accurately store all numbers. This is a limitation with the IEEE floating point system. In order to have faithful precision you need to use a more advanced math library.
If you don't need precision past a certain point, then perhaps decimal will work better for you. It has a higher precision than double.

For the similar issue, I end up using the following implementation which seems to success most of my test case (up to 5 digit precision):
public static double roundValue(double rawValue, double valueTick)
{
if (valueTick <= 0.0) return 0.0;
Decimal val = new Decimal(rawValue);
Decimal step = new Decimal(valueTick);
Decimal modulo = Decimal.Round(Decimal.Divide(val,step));
return Decimal.ToDouble(Decimal.Multiply(modulo, step));
}

Sometimes the result is more precise than you would expect from strict:FP IEEE 754.
That's because HW uses more bits for the computation.
See C# specification and this article
Java has strictfp keyword and C++ have compiler switches. I miss that option in .NET

Related

Rounding the SIGNIFICANT digits in a double, not to decimal places [duplicate]

This question already has answers here:
Round a double to x significant figures
(17 answers)
Closed 7 years ago.
I need to round significant digits of doubles. Example
Round(1.2E-20, 0) should become 1.0E-20
I cannot use Math.Round(1.2E-20, 0), which returns 0, because Math.Round() doesn't round significant digits in a float, but to decimal digits, i.e. doubles where E is 0.
Of course, I could do something like this:
double d = 1.29E-20;
d *= 1E+20;
d = Math.Round(d, 1);
d /= 1E+20;
Which actually works. But this doesn't:
d = 1.29E-10;
d *= 1E+10;
d = Math.Round(d, 1);
d /= 1E+10;
In this case, d is 0.00000000013000000000000002. The problem is that double stores internally fractions of 2, which cannot match exactly fractions of 10. In the first case, it seems C# is dealing just with the exponent for the * and /, but in the second case it makes an actual * or / operation, which then leads to problems.
Of course I need a formula which always gives the proper result, not only sometimes.
Meaning I should not use any double operation after the rounding, because double arithmetic cannot deal exactly with decimal fractions.
Another problem with the calculation above is that there is no double function returning the exponent of a double. Of course one could use the Math library to calculate it, but it might be difficult to guarantee that this has always precisely the same result as the double internal code.
In my desperation, I considered to convert a double to a string, find the significant digits, do the rounding and convert the rounded number back into a string and then finally convert that one to a double. Ugly, right ? Might also not work properly in all case :-(
Is there any library or any suggestion how to round the significant digits of a double properly ?
PS: Before declaring that this is a duplicate question, please make sure that you understand the difference between SIGNIFICANT digits and decimal places
The problem is that double stores internally fractions of 2, which cannot match exactly fractions of 10
That is a problem, yes. If it matters in your scenario, you need to use a numeric type that stores numbers as decimal, not binary. In .NET, that numeric type is decimal.
Note that for many computational tasks (but not currency, for example), the double type is fine. The fact that you don't get exactly the value you are looking for is no more of a problem than any of the other rounding error that exists when using double.
Note also that if the only purpose is for displaying the number, you don't even need to do the rounding yourself. You can use a custom numeric format to accomplish the same. For example:
double value = 1.29e-10d;
Console.WriteLine(value.ToString("0.0E+0"));
That will display the string 1.3E-10;
Another problem with the calculation above is that there is no double function returning the exponent of a double
I'm not sure what you mean here. The Math.Log10() method does exactly that. Of course, it returns the exact exponent of a given number, base 10. For your needs, you'd actually prefer Math.Floor(Math.Log10(value)), which gives you the exponent value that would be displayed in scientific notation.
it might be difficult to guarantee that this has always precisely the same result as the double internal code
Since the internal storage of a double uses an IEEE binary format, where the exponent and mantissa are both stored as binary numbers, the displayed exponent base 10 is never "precisely the same as the double internal code" anyway. Granted, the exponent, being an integer, can be expressed exactly. But it's not like a decimal value is being stored in the first place.
In any case, Math.Log10() will always return a useful value.
Is there any library or any suggestion how to round the significant digits of a double properly ?
If you only need to round for the purpose of display, don't do any math at all. Just use a custom numeric format string (as I described above) to format the value the way you want.
If you actually need to do the rounding yourself, then I think the following method should work given your description:
static double RoundSignificant(double value, int digits)
{
int log10 = (int)Math.Floor(Math.Log10(value));
double exp = Math.Pow(10, log10);
value /= exp;
value = Math.Round(value, digits);
value *= exp;
return value;
}

Rounding to specfic digits fails with this double-precision value

I'm attempting to truncate a series of double-precision values in C#. The following value fails no matter what rounding method I use. What is wrong with this value that causes both of these methods to fail? Why does even Math.Round fail to correctly truncate the number? What method can be used instead to correctly truncate such values?
The value :
double value = 0.61740451388888251;
Method 1:
return Math.Round(value, digits);
Method 2:
double multiplier = Math.Pow(10, decimals)
return Math.Round(value * multiplier) / multiplier;
Fails even in VS watch window!
Double is a floating binary point type. They are represented in binary system (like 11010.00110). When double is presented in decimal system it is only an approximation as not all binary numbers have exact representation in decimal system. Try for example this operation:
double d = 3.65d + 0.05d;
It will not result in 3.7 but in 3.6999999999999997. It is because the variable contains a closest available double.
The same happens in your case. Your variable contains closest available double.
For precise operations double/float is not the most fortunate choice.
Use double/float when you need fast performance or you want to operate on larger range of numbers, but where high precision is not required. For instance, it is perfect type for calculations in physics.
For precise decimal operations use, well, decimal.
Here is an article about float/decimal: http://csharpindepth.com/Articles/General/FloatingPoint.aspx
If you need a more exact representation of the number you might have to use the decimal type, which has more precision but smaller range (it's usually used financial calculations).
More info on when to use each here: https://stackoverflow.com/a/618596/1373170
According to this online tool which gives the binary representation of doubles, the two closest double values to 0.62 are:
6.19999999999999995559107901499E-1 or 0x3FE3D70A3D70A3D7
link
6.20000000000000106581410364015E-1 or 0x3FE3D70A3D70A3D8
link
I'm not sure why neither of these agree with your value exactly, but like the others said, it is likely a floating point representation issue.
I think you are running up against the binary limit of a double-precision float (64 bits). From http://en.wikipedia.org/wiki/Double-precision_floating-point_format, a double only gives between 15-17 significant digits.

Number type suffixes in C#

I searched for this question first before posting, but all I got was based on C++.
Here is my question:
Is a double with f suffix normal in c#? If yes, why and how is this possible?
Have a look at this code:
double d1 = 1.2f;
double d2 = 2.0f;
Console.WriteLine("{0}", d2 - d1);
decimal dm1 = 1.2m;
decimal dm2 = 2.0m;
Console.WriteLine("{0}", dm2 - dm1);
The answers for the first calculation is 0.799999952316284 with f suffix instead of 0.8. Also, when I change the f to a d which I think should be the normal way, it gives a correct answer of 0.8.
The right hand expression is evaluated as float and then "deposited" in a double variable. Nothing wrong or weird here. I think the difference in result has to do with the precision of the two data types.
Referring to your appreciation of the "correct answer", the fact that 0.8 came out "correct" is not because you changed from a float literal to a double literal. That's just a better approximation of the result. The "correct" result is indeed coming from the second expression, the one using decimal types.
The f suffix stand for float and not double. So 1.2f is a single precission floating point number which will be saved to a double directly after creating it because of an implicit cast to double.
The inprecission you are getting seems to be happening there and not at the calculation as it seems to be working with 1.2d.
Such behaviour is normal when using floating-point values. Use decimal if you do not want such behaviour as you already did in you examples yourself...
Double and Float both are binary numbers.
The Problem is not their precision but the kind of numbers they can store in an exact manner, which must be binary, too. Change 1.2f to 0.5f of 0.25f or 0.125f and so an and you will see 'correct' results. But any number with different factorials must be stored in an approximation. There is a '3' hidden in the 1.2 and you can't store in in a float or double. If you try, only an approximation will be stored.
Decimals are actually storing decimal digits and you won't see any approximations there as long as you don't leave the decimal realm. If you try to store, say, 1/3 in a decimal, it'll have to approximate as well..

Comparing floats as string! can I? Is it valid approach?

In order to check if difference between two float numbers is 0.01 I do this
if ((float1 - float.Parse(someFloatAsStringFromXML).ToString(), System.Globalization.CultureInfo.InvariantCulture)).ToString() == "0,01000977")
Is this type of "approach" is acceptable? Is there better way? How to?
p.s. I'm very new to c# and strong typing languages! So, if you have more than brief explanation, I would love to read it!
I forgot to mention, that numbers are "353.58" and "353.59". I have them as strings with dot "." not "," that is the reason why I use float.Parse
Firtly, you should always compare numbers as their underlying types (float, double, decimal), NOT as strings.
Now, you might think that you can compare like so:
float floatFromXml = float.Parse(someFloatAsStringFromXML);
if (Math.Abs(float1 - floatFromXml) == 0.01)
My example works as follows:
Firstly, calculate the difference between the two values:
float1 - floatFromXml
Then take the absolute value of that (which just removes the minus sign)
Math.Abs(float1 - floatFromXml)
Then see if that value is equal to 0.01:
if (Math.Abs(float1 - floatFromXml) == 0.01)
And if you don't want to ignore the sign, you wouldn't do the Math.Abs() part:
if ((float1 - floatFromXml) == 0.01)
But that won't work for your example because of rounding errors!
Because you are using floats (and this would apply to doubles too) you are going to get rounding errors which makes comparing the difference to 0.01 impossible. It will be more like 0.01000001 or some other slightly wrong value.
To fix that, you have to compare the actual difference to the target difference, and if that is only different by a tiny amount you say "that'll do".
In the following code, target is the target difference that you are looking for. In your case, it is 0.01.
Then epsilon is the smallest amount by which target can be wrong. In this example, it is 0.00001.
What we are saying is "if the difference between the two numbers is within 0.00001 of 0.1, then we will consider it as matching a difference of 0.1".
So the code calculates the actual difference between the two numbers, difference, then it sees how far away that difference is from 0.01 and if it is within 0.00001 it prints "YAY".
using System;
namespace Demo
{
class Program
{
void Run()
{
float f1 = 353.58f;
float f2 = 353.59f;
if (Math.Abs(f1 - f2) == 0.01f)
Console.WriteLine("[A] YAY");
else
Console.WriteLine("[A] Oh dear"); // This gets printed.
float target = 0.01f;
float difference = Math.Abs(f2 - f1);
float epsilon = 0.00001f; // Any difference smaller than this is ok.
float differenceFromTarget = Math.Abs(difference - target);
if (differenceFromTarget < epsilon)
Console.WriteLine("[B] YAY"); // This gets printed.
else
Console.WriteLine("[B] Oh dear");
}
static void Main()
{
new Program().Run();
}
}
}
However, the following is probably the answer for you
Alternatively, you can use the decimal type instead of floats, then the direct comparison will work (for this particular case):
decimal d1 = 353.58m;
decimal d2 = 353.59m;
if (Math.Abs(d1 - d2) == 0.01m)
Console.WriteLine("YAY"); // This gets printed.
else
Console.WriteLine("Oh dear");
You give an example of your two numbers
353.58
353.59
These numbers cannot be exactly represented in binary floating point format. Also, the value 0.01 cannot be exactly represented in binary floating point format. Please read What Every Computer Scientist Should Know About Floating-Point Arithmetic.
So, for example, when you try to represent 353.58 as a float, there is no float with that value and you get the closest float value which happens to be 353.579986572265625.
In my view you are using the wrong data type to represent these values. You need to be using a decimal format. That will allow you to represent these values exactly. In C# you use the decimal type.
Then you can write:
decimal value1 = 353.58m;
decimal value2 = 353.59m;
Debug.Assert(Math.Abs(value2-value1) == 0.01m);
Use decimal.Parse() or decimal.TryParse() to convert from your textual representation of the number into a decimal value.
In direct answer to your question, can you do this? Yes. Is it a valid approach? Almost certainly not.
If you give some more context, you will get more detailed answers.
This isn't a good way of doing it. There's no need to compare it to a string and you don't gain anything by doing it that way. Compare it to an actual float value.
The immediately visible problem with your approach there is it's not culture safe. In the UK we use . instead of , as the decimal point - so the result of .ToString() wouldn't match at all. But aside from that it's just bad practice to do that without a good reason.

Converting to double is giving not accurate number

Convert.ToDouble is adding zeros and 1 like in this picture:
Why it is turning from 21.62 to 21.620000000000001 ?
Is this about floating point issue?
Double (and Float) are floating-point types, and in a binary system will have some imprecision.
If you need more precise comparisons use decimal instead. If you're just doing calculations double should be fine. If you need to compare doubles for absolute equiality then compare the absolute value of the difference to some small constant:
if (a == b) // not reliable for floating point
{
....
}
double EPSILON = 0.0000001;
if (Math.Abs(a-b) < EPSILON)
{
....
}
The floating point numbers have some problems of approximation.
This is because decimal fraction like 0,00001 can't be represented exactly on a binary system (where fractional numbers are represented in module q/p).
The problem is intrinsic.
In short, yes; between any two bases (in this case, 2 and 10), there are always values that can expressed w/ a finite number of "decimal" (binimal?) places in one that cannot in the other.
Rounding errors like this are common in almost every high level programming language. In Java, to get around this you use a class called BigDecimal if you need guaranteed accuracy. Otherwise, you can just write a method that rounds a decimal to the nearest place you need.

Categories

Resources