How can you get the number of digits contained in a double? - c#

I'm trying to get the number of digits in the following double value: 56.46855976 without using converting it to a string (and simply replacing the "." with a "").
Anybody got any ideas?

Count how often you must divide the number by 10 until it's smaller than 1 -> that gives you the digits before the decimal point.
Then count how often you must multiply the original number by 10 until it equals the Math.Floor-result -> that gives you the digits behind the decimal points.
Add. Be glad.
Edit: As Joey points out, there is some uncertianity in it. Define a maximum number of digits beforehand so you don't create an infinite loop.
On the other hand - "How long is the coast of Denmark?"...

/// Returns how many digits there are to the left of the .
public static int CountDigits(double num) {
int digits = 0;
while (num >= 1) {
digits++;
num /= 10;
}
return digits;
}
As Martin mentioned, counting to the right of the . is pointless.
Tests:
MathPlus.CountDigits(56.46855976) -> 2
MathPlus.CountDigits((double)long.MaxValue + 1) -> 19
MathPlus.CountDigits(double.MaxValue) -> 309

Converting to a string might be the best option you have. Remember that doubles are represented in Base 2 internally. Therefore the decimal representation you see is only an approximation of the actually stored value (except for integers up to 253) which is a sum of individual powers of 2.
So trying to figure out the number of decimal digits from the binary representation is certainly not an easy or trivial task – especially since the framework might also apply rounding to make numbers like 3.999999999998 appear like 4.0 since they appear to have more precision than there actually is.

Related

How to print all the decimal points without rounding up?

I want to calculate wind_chill by getting temp and wind_speed from user.
All variables are declared as double.
wind_chill = 35.74 + 0.6215 * temp + (0.4275 * temp - 35.75) * System.Math.Pow(wind_speed, 0.16);
I am getting this O/P:-
Enter temperature and wind Speed: 20 7 wind_chill is:11.03490062551
But I want to print all the decimal number till last without round up.
Expected O/P:-
wind_chill = 11.034900625509998
By declaring variables as decimal and converting all the values in decimal:
I am getting this output:
wind_chill = 11.034900625510096
Still not matching with the expected one. I searched But I didn't get my answer.How to get expected output?
Calculate the value with doubles. It is just that .NET will format the value to 15 characters when printing. Use R format to get all digits.
var wind_chill = WindChillDbl(20.0, 7.0);
Console.WriteLine(String.Format("{0:R}", wind_chill));
public static double WindChillDbl(double temp, double wind_speed)
{
return 35.74 + 0.6215 * temp + (0.4275 * temp - 35.75) * System.Math.Pow(wind_speed, 0.16);
}
"By default, the return value only contains 15 digits of precision although a maximum of 17 digits is maintained internally. If the value of this instance has greater than 15 digits, ToString returns PositiveInfinitySymbol or NegativeInfinitySymbol instead of the expected number. If you require more precision, specify format with the "G17" format specification, which always returns 17 digits of precision, or "R", which returns 15 digits if the number can be represented with that precision or 17 digits if the number can only be represented with maximum precision." MSDN
You can inspect your calculation to see that the values returned for most of the expression are accurate. The problem is with the accuracy of System.Math.Pow(wind_speed, 0.16);. If you look at wolframalpha for that input, there are signifcantly more digits provided than the 1.36526100641507 returned by Math.Pow.
The reason for this is because Math.pow uses float point types which are inaccurate by design. You may also be able to use BigInteger and figure out a way to make your equation work with that.
You can resolve this in a couple of ways:
Use BigRational
Rework the equation to somehow use BigInteger
See this question: What is the equivalent of the Java BigDecimal class in C#?, specifically this answer: https://stackoverflow.com/a/13813535/2127492
If you do go with the BigDecimal class provided in that answer, you will be able to make use of the method BigDecimal Pow(double basis, double exponent) to improve the accuracy your calculation.
You can see your calculation with the above class here.

Double comparison precision loss in C#, accuracy loss happening when adding subtracting doubles

Just started learning C#. I plan to use it for heavy math simulations, including numerical solving. The problem is I get precision loss when adding and subtracting double's, as well as when comparing. Code and what it returns (in comments) is below:
namespace ex3
{
class Program
{
static void Main(string[] args)
{
double x = 1e-20, foo = 4.0;
Console.WriteLine((x + foo)); // prints 4
Console.WriteLine((x - foo)); // prints -4
Console.WriteLine((x + foo)==foo); // prints True BUT THIS IS FALSE!!!
}
}
}
Would appreciate any help and clarifications!
What puzzles me is that (x + foo)==foo returns True.
Take a look at the MSDN reference for double: https://msdn.microsoft.com/en-AU/library/678hzkk9.aspx
It states that a double has a precision of 15 to 16 digits.
But the difference, in terms of digits, between 1e-20 and 4.0 is 20 digits. The mere act of trying to add or subtract 1e-20 to or from 4.0 simply means that the 1e-20 is lost because it cannot fit within the 15 to 16 digits of precision.
So, as far as double is concerned, 4.0 + 1e-20 == 4.0 and 4.0 - 1e-20 == 4.0.
Additional to Enigmativity's answer:
To make this works you need more precision, and that decimal with a precision of 28 to 29 digits and base of 10:
decimal x = 1e-20m, foo = 4.0m;
Console.WriteLine((x + foo)); // prints 4.00000000000000000001
Console.WriteLine((x - foo)); // prints -3.99999999999999999999
Console.WriteLine((x + foo) == foo); // prints false.
But beware that it's true that decimal has bigger precision but has a lower range. see more about decimal here
What you are looking for is probably the Decimal Structure (https://msdn.microsoft.com/en-us/library/system.decimal.aspx).
Doubles can't right represent that kind of values with the precision you are looking for (C# double to decimal precision loss). Instead, try using the Decimal class, like so:
decimal x = 1e-20M, foo = 4.0M;
Console.WriteLine(Decimal.Add(x, foo)); //prints 4,0000000000000000001
Console.WriteLine(Decimal.Add(x, -foo)); //prints -3,9999999999999999999
Console.WriteLine(Decimal.Add(x, foo) == foo); // prints false
This is not a problem with C#, but with your computer. It's not really complicated or hard to understand, but it's a long read. You should read this article, if you want an actual in depth knowledge of how your computer works.
An excellent TLDR site that imho is a better intro to the subject than the aforementioned article is this one:
http://floating-point-gui.de/
I'll provide you with a very short explanation of what's going on, but you should certainly read at least that site, to avoid trouble in the future, since your field of application will require such knowledge in depth.
What happens is the following: you have 1e-20, which is a smaller number than 1.11e-16. That other number, is called the machine epsilon for double precision on your computer (most likely). If you add a number equal to or larger than 1, to something smaller than the machine epsilon, it will be rounded off, back to the large number. That's due to the IEEE 754 representation. This means that after the addition happens, the result, which is "correct" (as if you had infinite precision), the result is stored in a format of limited/finite precision, that rounds 4.00....001 to 4, because the error of the rounding is smaller than 1.11e-16, thus is considered acceptable.

Get number of digits in an unsigned long integer c#

I'm trying to determine the number of digits in a c# ulong number, i'm trying to do so using some math logic rather than using ToString().Length. I have not benchmarked the 2 approaches but have seen other posts about using System.Math.Floor(System.Math.Log10(number)) + 1 to determine the number of digits.
Seems to work fine until i transition from 999999999999997 to 999999999999998 at which point, it i start getting an incorrect count.
Has anyone encountered this issue before ?
I have seen similar posts with a Java emphasis # Why log(1000)/log(10) isn't the same as log10(1000)? and also a post # How to get the separate digits of an int number? which indicates how i could possibly achieve the same using the % operator but with a lot more code
Here is the code i used to simulate this
Action<ulong> displayInfo = number =>
Console.WriteLine("{0,-20} {1,-20} {2,-20} {3,-20} {4,-20}",
number,
number.ToString().Length,
System.Math.Log10(number),
System.Math.Floor(System.Math.Log10(number)),
System.Math.Floor(System.Math.Log10(number)) + 1);
Array.ForEach(new ulong[] {
9U,
99U,
999U,
9999U,
99999U,
999999U,
9999999U,
99999999U,
999999999U,
9999999999U,
99999999999U,
999999999999U,
9999999999999U,
99999999999999U,
999999999999999U,
9999999999999999U,
99999999999999999U,
999999999999999999U,
9999999999999999999U}, displayInfo);
Array.ForEach(new ulong[] {
1U,
19U,
199U,
1999U,
19999U,
199999U,
1999999U,
19999999U,
199999999U,
1999999999U,
19999999999U,
199999999999U,
1999999999999U,
19999999999999U,
199999999999999U,
1999999999999999U,
19999999999999999U,
199999999999999999U,
1999999999999999999U
}, displayInfo);
Thanks in advance
Pat
log10 is going to involve floating point conversion - hence the rounding error. The error is pretty small for a double, but is a big deal for an exact integer!
Excluding the .ToString() method and a floating point method, then yes I think you are going to have to use an iterative method but I would use an integer divide rather than a modulo.
Integer divide by 10. Is the result>0? If so iterate around. If not, stop.
The number of digits is the number of iterations required.
Eg. 5 -> 0; 1 iteration = 1 digit.
1234 -> 123 -> 12 -> 1 -> 0; 4 iterations = 4 digits.
I would use ToString().Length unless you know this is going to be called millions of times.
"premature optimization is the root of all evil" - Donald Knuth
From the documentation:
By default, a Double value contains 15
decimal digits of precision, although
a maximum of 17 digits is maintained
internally.
I suspect that you're running into precision limits. Your value of 999,999,999,999,998 probably is at the limit of precision. And since the ulong has to be converted to double before calling Math.Log10, you see this error.
Other answers have posted why this happens.
Here is an example of a fairly quick way to determine the "length" of an integer (some cases excluded). This by itself is not very interesting -- but I include it here because using this method in conjunction with Log10 can get the accuracy "perfect" for the entire range of an unsigned long without requiring a second log invocation.
// the lookup would only be generated once
// and could be a hard-coded array literal
ulong[] lookup = Enumerable.Range(0, 20)
.Select((n) => (ulong)Math.Pow(10, n)).ToArray();
ulong x = 999;
int i = 0;
for (; i < lookup.Length; i++) {
if (lookup[i] > x) {
break;
}
}
// i is length of x "in a base-10 string"
// does not work with "0" or negative numbers
This lookup-table approach can be easily converted to any base. This method should be faster than the iterative divide-by-base approach but profiling is left as an exercise to the reader. (A direct if-then branch broken into "groups" is likely quicker yet, but that's way too much repetitive typing for my tastes.)
Happy coding.

C# - Incrementing a double value (1.212E+25)

I have a double value that equals 1.212E+25
When I throw it out to text I do myVar.ToString("0000000000000000000000")
The problem is even if I do myVar++ 3 or 4 times the value seems to stay the same.
Why is that?
That is because the precision of a double is not sufficient. It simply can't hold that many significant digits.
It will not fit into a long, but probably into a Decimal.
But... do you really need this level of precision?
To expand on the other answer the smallest increase you can make to a double is one Unit in the Last Place, or ULP, as double is a floating point type then the size of an ULP changes, at 1E+25 it will be about 1E+10.
as you can see compared to 1E+10 incrementing by 1 really might as well be adding nothing. which is exactly what double will do, so it wouldnt matter if you tried it 10^25 times it still won't increase unless you try to increase by at least 1 ULP
if incrementing by an ULP is useful you can do this by casting the bits to long and back here is a quick extension method to do that
public static double UlpChange(this double val, int ulp)
{
if (!double.IsInfinity(val) && !double.IsNaN(val))
{
//should probably do something if we are at max or min values
//but its not clear what
long bits = BitConverter.DoubleToInt64Bits(val);
return BitConverter.Int64BitsToDouble(bits + ulp);
}
return val;
}
double (Double) holds about 16 digits of precision and long (Int64) about 18 digits.
Neither of these appear to have sufficient precision for your needs.
However decimal (Decimal) holds up to 30 digits of precision. Although this appears to be great enough for your needs I'd recommend caution in case your requirement grows even larger. In that case you may need a third party numeric library.
Related StackOverflow entries are:
How can I represent a very large integer in .NET?
Big integers in C#
You may want to read the Floating-Point Guide to understand how doubles work.
Basically, a double only has about 16 decimal digits of precision. At a magnitude of 10^25, an increase of 1.0 is below the threshold of precision and gets lost. Due to the binary representation, this may not be obvious.
The smallest increment that'll work is 2^30+1 which will actually increment the double by 2^31. You can test this kind of thing easily enough with LINQPad:
double inc = 1.0;
double num = 1.212e25;
while(num+inc == num) inc*=2;
inc.Dump(); //2147483648 == 2^31
(num+inc == num).Dump(); //false due to loop invariant
(num+(inc/2.0) == num).Dump();//true due to loop invariant
(num+(inc/2.0+1.0) == num).Dump();//false - 2^30+1 suffices to change the number
(num+(inc/2.0+1.0) == num + inc).Dump();//true - 2^30+1 and 2^31 are equiv. increments
((num+(inc/2.0+1.0)) - num == inc ).Dump();//true - the effective increment is 2^31
Since a double is essentially a binary number with limited precision, that means that the smallest possible increment will itself always be a power of two (this increment can be determined directly from the bit-pattern of the double, but it's probably clearer to do so with a loop as above since that's portable across float, double and other floating point representations (which don't exist in .NET).

Is dotNet decimal type vulnerable for the binary comparison error?

One error I stumble upon every few month is this one:
double x = 19.08;
double y = 2.01;
double result = 21.09;
if (x + y == result)
{
MessageBox.Show("x equals y");
}
else
{
MessageBox.Show("that shouldn't happen!"); // <-- this code fires
}
You would suppose the code to display "x equals y" but that's not the case.
The short explanation is that the decimal places are, represented as a binary digit, do not fit into double.
Example:
2.625 would look like:
10.101
because
1-------0-------1---------0----------1
1 * 2 + 0 * 1 + 1 * 0.5 + 0 * 0.25 + 1 * 0,125 = 2.65
And some values (like the result of 19.08 plus 2.01) cannot be be represented with the bits of a double.
One solution is to use a constant:
double x = 19.08;
double y = 2.01;
double result = 21.09;
double EPSILON = 10E-10;
if ( x + y - result < EPSILON )
{
MessageBox.Show("x equals y"); // <-- this code fires
}
else
{
MessageBox.Show("that shouldn't happen!");
}
If I use decimal instead of double in the first example, the result is "x equals y".
But I'm asking myself If this is because of "decimal" type is not vulnerable of this behaviour or it just works in this case because the values "fit" into 128 bit.
Maybe someone has a better solution than using a constant?
Btw. this is not a dotNet/C# problem, it happens in most programming languages I think.
Decimal will be accurate so long as you stay within values which are naturally decimals in an appropriate range. So if you just add and subtract, for example, without doing anything which would skew the range of digits required too much (adding a very very big number to a very very small number) you will end up with easily comparable results. Multiplication is likely to be okay too, but I suspect it's easier to get inaccuracies with it.
As soon as you start dividing, that's where the problems can come - particularly if you start dividing by numbers which include prime factors other than 2 or 5.
Bottom line: it's safe in certain situations, but you really need to have a good handle on exactly what operations you'll be performing.
Note that it's not the 128-bitness of decimal which is helping you here - it's the representation of numbers as floating decimal point values rather than floating binary point values. See my articles on .NET binary floating point and decimal floating point for more information.
System.Decimal is just a floating point number with a different base so, in theory, it is still vulnerable to the sort of error you point out. I think you just happened on a case where rounding doesn't happen. More information here.
Yes, the .NET System.Double structure is subject to the problem you describe.
from http://msdn.microsoft.com/en-us/library/system.double.epsilon.aspx:
Two apparently equivalent floating-point numbers might not compare equal because of differences in their least significant digits. For example, the C# expression, (double)1/3 == (double)0.33333, does not compare equal because the division operation on the left side has maximum precision while the constant on the right side is precise only to the specified digits. If you create a custom algorithm that determines whether two floating-point numbers can be considered equal, you must use a value that is greater than the Epsilon constant to establish the acceptable absolute margin of difference for the two values to be considered equal. (Typically, that margin of difference is many times greater than Epsilon.)

Categories