This question already has answers here:
Why is floating point arithmetic in C# imprecise?
(3 answers)
Closed 9 years ago.
I am creating a float point control where you can increment by 10, 100, 1000 etc.
The text I display in the control is myFloatNumber.ToString("E");
as I was increasing a number I noticed:
1.0E+1f = 1.0E+1f
1.0E+2f = 1.0E+2f
1.0E+3f = 1.0E+3f
......
......
1.0E+25f = 1.0E+25f
1.0E+26f = 1.0E+26f
1.0E+27f = 1.0E+27f
1.0E+28f = 9.999999E+27f // Why!!?????
The solution to this problem is to use a double but I am very curios as to why is this behavior?
here is the code:
string str1 = (1.0E27f).ToString(); // str1 = "1E+27"
string str2 = (1.0E28f).ToString(); // str2 = "9.999999E+27";
string str3 = (1.0E29f).ToString(); // str3 = "1E+29"
You have to take into account that 1.0e28f is a decimal (base 10) representation.
Converting this string (either a literal in source code or value in a data file) to a floating point imply a conversion to base 2 (the float internal representation).
The 3 nearest floating point values in the neighbourhood of 10^28 are exactly
0.9999998261528069050908803072e28f
0.9999999442119689768320106496e28f <- the nearest one
1.0000000622711310485731409920e28f
or in base 2:
1.00000010011111100111000 * 2^93
1.00000010011111100111001 * 2^93
1.00000010011111100111010 * 2^93
The nearest one (in the middle) is chosen and that is the float in memory, not 1.0e28.
When you request ToString(), you are converting back 0.9999999442119689768320106496e28f to a decimal form.
It sounds like default format is to print 1 decimal before the floating point separator, and 6 decimals after the floating point (much like C printf %f format)
The nearest such decimal to 0.9999999442119689768320106496e28f is indeed 9.999999e27, that's pretty straight forward.
EDIT
Note that 5^3 is near 2^7, since single precision float has 24 bits significand, and 24*3/7 is approximately 10, the maximum power of 5 represented exactly in single precision float is 5^10.
Hence, the maximum power of 10 representable exactly in float is 1.0e10.
Every power from 11 to 27 did print like you expect somehow by luck.
Well, not exactly by luck, but because the roundoff error was less than 0.5*10^(n-7).
EDIT 2 - about double
And double does not really "solve" things, because with 53 bits significand you can go up to 53*3/7 that is 10^22 without rounding error.
Every power of 10 from 23 and above are subject to round off.
It somehow solves however if you keep printing 6 decimals, because the round off occurs around the 15th digit.
Related
This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 23 hours ago.
I have project that should calculate an equation and it checks every number to see if it fits the equation. every time it pluses the number by 0.00001. but sometiomes it gets random on next decimals for example 0.000019999999997.
I even tried a breakpoint at that line. When I am right on that line it is 1.00002 for example but when I go to next line it is 1.00002999999997.
I don't why it is like that. I even tried smaller numbers like 0.001.
List<double> anwsers = new List<double>();
double i = startingPoint;
double prei = 0;
double preAbsoluteValueOfResult = 0;
while (i <= endingPoint)
{
prei = i;
i += 0.001;
}
Added a call to Math.Round to round the result to 3 decimal places before adding it to the answers list. This ensures that the values in the list will always have exactly 3 digits past the decimal point.
List<double> answers = new List<double>();
double i = startingPoint;
double prei = 0;
double preAbsoluteValueOfResult = 0;
while (i <= endingPoint)
{
prei = i;
i += 0.001;
double result = Math.Round(prei, 3);
answers.Add(result);
}
Computers don't store exact floating point numbers. A float is usually 32 bits or 64 bits and cannot store numbers to arbitrary precision.
If you want dynamic precision floating point, use a number library like GMP.
There are some good video's on this https://www.youtube.com/watch?v=2gIxbTn7GSc
But esentially its because there arnt enough bits. if you have a number being stored e.g. 1/3 it can only store so many decimal places, so its actually going to be something like 0.3334. Which means when you do something like 1/3 + 1/3 it isnt going to equal 2/3 like one might expect, it would equal 0.6668
To summarize, using the decimal type https://learn.microsoft.com/en-us/dotnet/api/system.decimal?view=net-7.0 Over double, should fix your issues.
TL;DR: You should use a decimal data type instead of a float or double. You can declare your 0.001 as a decimal by adding an m after the value: 0.001m
The double data type you chose relies on a representation of decimal numbers via a fraction of two integers. It is great for storing large decimal numbers with little memory, but it also means your number gets rounded to the closest value which can be represented by such a fraction. A decimal on the other hand will store the information in a different way, which will more closely represent what you intuitively expect from decimal numbers.
More information about float values: https://floating-point-gui.de/
More information about numeric types declaration: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types
The documentation also explains:
Just as decimal fractions are unable to precisely represent some fractional values (such as 1/3 or Math.PI), binary fractions are unable to represent some fractional values. For example, 1/10, which is represented precisely by .1 as a decimal fraction, is represented by .001100110011 as a binary fraction, with the pattern "0011" repeating to infinity. In this case, the floating-point value provides an imprecise representation of the number that it represents. Performing additional mathematical operations on the original floating-point value often tends to increase its lack of precision.
This question already has answers here:
Is floating point math broken?
(31 answers)
Rounding of float values
(2 answers)
Difference between decimal, float and double in .NET?
(18 answers)
Closed 4 years ago.
float ff = (float)31.15;
double dd = 31.15;
var frst = Math.Round(ff, 1, MidpointRounding.AwayFromZero);
var drst = Math.Round(dd, 1, MidpointRounding.AwayFromZero);
frst: 31.1
drst: 31.2
Can someone explain why?
Well, Math.Round wants double, not float, that's why
Math.Round(ff, 1, MidpointRounding.AwayFromZero);
equals to
Math.Round((double)ff, 1, MidpointRounding.AwayFromZero);
and if we inspect (double)ff value
Console.Write(((double)ff).ToString("R"));
we'll see round up errors in action
31.149999618530273
Finally, Math.Round(31.149999618530273, 1, MidpointRounding.AwayFromZero) == 31.1 as expected
In floating point, all numbers are represented internally as fractions where the denominator is a power of 2.
(This is a similar way to how decimals are actually fractions with power-of-10 denominators. So 31.15 is just a way of writing the fraction 3115/100)
In floating point, 31.15 must be represented internally as a binary number. The closest binary fraction is: 1111.1001001100110011001100110011001100110011001100110011001100...repeating
The 1100 recurs (repeats forever), and so the number will be truncated depending on whether it is stored in a double or a float. In a float it is truncated to 24 digits, and in a double to 53.
Exact: 1111.100100110011001100110011001100110011001100110011001100110011001100...forever
Float: 1111.10010011001100110011
Double: 1111.1001001100110011001100110011001100110011001100110
Therefore you can see that the double that this number converts to, is actually slightly larger than the float it converts to. So it is clear that it won't necessarily round to the same number, since it is not the same number to begin with.
This question already has answers here:
C# is rounding down divisions by itself
(10 answers)
Closed 8 years ago.
I am working in Unity3D with C# and I get a weird result. Can anyone tell me why my code equals 0?
float A = 1 / 90;
The literals 1 and 90 are interpreted as an int. So integer division is used. After that the result is converted to a float.
In general C# will read all sequences (without decimal dot) of digits as an int. An int will be converted to a float if necessary. But before the assignment, that's not necessary. So all calculations in between are done as ints.
In other words, what you've written is:
float A = (float) ((int) 1)/((int) 90)
(made it explicit here, this is more or less what the compiler reads).
Now a division of two int's is processed such that it takes only the integral part into account. The integral part of 0.011111 is 0 thus zero.
If you however modify one of the literals to a floating point (1f, 1.0f, 90f,...) or both, this will work. Thus use one of these:
float A = 1/90.0f;
float A = 1.0f/90;
float A = 1.0f/90.0f;
In that case, floating point division will be performed. Which takes into account both parts.
etc.
This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 8 years ago.
I'm having problems with a Rounding issue in c#. I would like to round the result of a calculation up to 4 decimals (awayfromzero). If I use Math.Round(variable,...) it rounds down, if I enter the result manually, it rounds up.. I have no idea why..
What am I doing wrong? The result of the below code is:
Rounded: 591.24575 591.2457 - 591.2458
double number1 = 1136.81;
double number2 = 4.00;
double number3 = 2182.257;
double result = (number1 * number2 - number3) / 4;
Console.WriteLine("Rounded: " +result+" " + Math.Round(result, 4, MidpointRounding.AwayFromZero) + " - " + Math.Round(591.24575, 4, MidpointRounding.AwayFromZero));
Console.ReadLine();
If you use my DoubleConverter, you can see the exact value of result:
Console.WriteLine(DoubleConverter.ToExactString(result));
That prints:
591.2457499999999299689079634845256805419921875
... which rounds to 591.2457 when rounded to 4 decimal places. When you just print result, it's printing it already rounded to a certain number of decimal places, which can affect the result if you then (mentally) rounded to 4 DP.
You can see this without any of the "normal" oddities of binary floating point. Consider this code:
decimal exact = 1.2345m;
decimal rounded2 = Math.Round(exact, 2, MidpointRounding.AwayFromZero);
decimal rounded3 = Math.Round(exact, 3, MidpointRounding.AwayFromZero);
decimal rounded3Then2 = Math.Round(rounded3, 2, MidpointRounding.AwayFromZero);
Console.WriteLine(rounded2); // 1.23
Console.WriteLine(rounded3); // 1.235
Console.WriteLine(rounded3Then2); // 1.24
In your code you weren't actually performing the "two roundings" - but you were mentally doing so by taking the printed value of result (591.24575) and assuming you could take that to be accurate in order to round it further.
result is not exactly 591.24575, it is some number that is very close to 591.24575 but it is just slightly smaller, something along the lines of 591.24574999999999, as a result of the fact that certain numbers that have a finite number of digits in base 10 cannot be represented with a finite number of digits in base 2. When you set the number exactly you are avoiding ever having a double set to one of those values as an intermediate value of your calculations.
If you are dealing with numbers that have a known fixed number of base 10 digits and it's important that you not have these precision errors than it may be appropriate to use Decimal in this context.
result doesn't have the value you think it does, it's being truncated by putting it in a string representation. As for why that is, read What Every Computer Scientist Should Read About Floating Point
Take a look while debugging:
result: 591.24574999999993
result.ToString(): "591,24575"
var x = dr["NationalTotal"].ToString();
gives me 333333333
var xxx = Convert.ToSingle(dr["NationalTotal"].ToString());
gives me 333333344
Any ideas why?
This happens because Single does not have enough precision to store your full number.
Double has more precision.
The floating point specification says that the 32 bit representation of a floating point number is
Thus, the largest integer that can be represented without loss of (integer) accuracy is 16777216 (0x1000000). A simple test program to convince you that this is so:
#include <stdio.h>
int main(void) {
float x;
unsigned long j;
j = 0x00FFFFFC;
int i;
x = j;
for(i=0; i<10;i++) printf("%ld + %d = %f\n", j, i, x+i);
}
Output:
16777212 + 0 = 16777212.000000
16777212 + 1 = 16777213.000000
16777212 + 2 = 16777214.000000
16777212 + 3 = 16777215.000000
16777212 + 4 = 16777216.000000
16777212 + 5 = 16777216.000000 <<< from here on, adding one more doesn't give the right answer
16777212 + 6 = 16777218.000000
16777212 + 7 = 16777220.000000
16777212 + 8 = 16777220.000000
16777212 + 9 = 16777220.000000
EDIT Based on the comments underneath the question, we slowly converged on the fact that you had two questions, not one.
The first: "Why is this happening?" is answered with the above. A single float simply is not able to represent 333333333 exactly, so you get the nearest representable value, which is 333333344.
The second: "How do I fix it?" was initially answered by me in the comments - I will reprise my answer here:
Your database floating point number is not typically single precision - in fact, by default it is double precision. Thus, you solve your problem by converting the string to double, and assigning it to a double precision variable:
double xxx = Convert.ToDouble(dr["NationalTotal"]);
I would like to refer you to http://floating-point-gui.de/ - "what every programmer should know about floating point". There are actually many guides out there with similar names. Essential reading if you ever stray from using just integers (and most people will, at some point in their programming career).
From the docs for System.Single:
All floating-point numbers have a limited number of significant digits, which also determines how accurately a floating-point value approximates a real number. A Double value has up to 7 decimal digits of precision, although a maximum of 9 digits is maintained internally.
It actually means Single here rather than Double, but the point is that you're trying to use it for 9 significant digits, and that's not guaranteed to be available. You can see this very easily without any parsing:
float f = 333333333f;
Console.WriteLine("{0:r", f); // Prints 333333344
("r" is the "round-trip" format specifier.)
In other words, the closest float value to the exact decimal value of 333333333 is 333333344.
If you were to use Double instead, that would probably retain all the digits, but that doesn't mean it's the right approach necessarily. It depends on the value. Is it actually always an integer? Maybe you should be using long. Is it a non-integer, but financial data (or some other "artificial" value)? If so, consider using decimal.
Also, why are you having to convert this to a string and parse it? What's the execution time type of dr["NationalTotal"]? You may be able to just cast - avoid using string conversions where you don't need to.