6

Method below should return a answer to "What decimal precision, quantity of trailing numbers, do this (double) value have?". I got it right when the values looks like 5900.43, 5900.043 and so on. When the method recieves 5900.00, it return 0, which is wrong (in my need).

   /// <summary>
   /// Return count of decimals after decimal-place 
   /// </summary>
   private int getDecimalCount(double val)
   {
        int count = 0;
        ...
        return count;
   }

When parameter 'val' is (samples of normal values to this method)

5900.00 return 2
5900.09 return 2
5900.000 return 3
5900.001 return 3
1.0 return 1
0.0000005 return 7
1.0000000 return 7
5900822 return 0

My problem is counting .0 .00 .000 and so on

Question:
How do I solve this? If this method isn't possible with doubles, how else?

[EDITED]
- Minor text change to make a more clear question,
- Language correction due to miss-used meaning of 'decimal places'

Independent
  • 2,924
  • 7
  • 29
  • 45
  • It would help if you give more information about exactly what you're trying to achieve. For example, what would you want to see for the value (1.0D + Double.Epsilon) = (1.0D + 4.94065645841247e-324)? – Joe Sep 08 '11 at 10:19
  • 99,09 is a typo? would be 99.09 rigth? – CB. Sep 08 '11 at 10:28
  • 1
    @Christian, typo or differences in culture, – Independent Sep 08 '11 at 10:45
  • @Joe The method must return the correct amount of decimal places. The conversions I've seen / tried so far appears to don't understand when there are only zeros in the decimal places . – Independent Sep 08 '11 at 10:47
  • After `double d1 = 54646.00` and `double d2 = 54646.0` , `d1` and `d2` have **exactly the same value**. If you want to keep track of supplied precision, you need to use `decimal`. – AakashM Sep 08 '11 at 11:13
  • @AkaakashM, they are of exact value, the point is to return the count of decimals! – Independent Sep 08 '11 at 11:23
  • Sorry, what is your question? – leppie Sep 08 '11 at 11:29
  • @Jonas It appears you're missing the point people are trying to make. I can give you four variables that would all answer the question "How many decimal places do this (double) value have?" differently but have the exact same value. The "count of decimals" is dependent on the **string** representation of the **double** value. – Rick Liddle Sep 08 '11 at 11:29
  • @leppie How do I count the amount of decimals, of a double? Oh.. I see.. decimal place is a: , – Independent Sep 08 '11 at 11:32
  • Just count the number of digits after the `.` and handle the single `.0` case. – leppie Sep 08 '11 at 11:34
  • @Rick, Thank's Your probably right. I really can't understand why I no longer want to know how many integers there are after the decimal place on the value (double) 5900.00.. – Independent Sep 08 '11 at 11:35
  • @leppie Yes! This is a move forward. How do I count the number of digits afte the '.' (which are the reason that this question exist)? – Independent Sep 08 '11 at 11:40
  • 2
    I understand that your **business** need is to determine the precision (number of decimal places) in a supplied value. What we're all trying to tell you is that from a **programmatic** standpoint, that can't be determined. What you CAN determine is how many characters exist in the fractional portion of a double precision floating point number represented by a string. You can't do what you're looking for without converting to a string because I can give you a nearly unlimited number of values that are all mathematically equal to each other and all answer your question differently. – Rick Liddle Sep 08 '11 at 11:56
  • @Rick, +1 for help (decimal precision). The question is here for my business purpose. How do I stringify and still keep the amount of decimals (even with 0)? – Independent Sep 08 '11 at 12:03
  • I'll say it again, `59` and `59.0000` as `double`s are **the same value**. Within the body of a method with the signature you've given, it is *not possible* to tell apart whether you were called with `getDecimalCount(59)` or `getDecimalCount(59.000000)`. – AakashM Sep 08 '11 at 13:19
  • @A, Thank's, that's exaggerated super-clear (even before this question). Then we need another method signature or another way to solve it. Right? How? The pasted code in the question was just a copy from how I (unsuccesfully) tried to solve it. – Independent Sep 08 '11 at 13:34
  • Jonas, will the values you're working with always use "." as the decimal separator, or do you need to account for how different cultures write decimal numbers? – Rick Liddle Sep 08 '11 at 13:53

4 Answers4

5

EDIT: Updated to simplify handling different cultures.

Similarly to @Miguel, here's how I would handle it:

public static int getDecimalCount(double dVal, string sVal, string culture)
{
    CultureInfo info = CultureInfo.GetCultureInfo(culture);

    //Get the double value of the string representation, keeping culture in mind
    double test = Convert.ToDouble(sVal, info);

    //Get the decimal separator the specified culture
    char[] sep = info.NumberFormat.NumberDecimalSeparator.ToCharArray();

    //Check to see if the culture-adjusted string is equal to the double
    if (dVal != test)
    {
        //The string conversion isn't correct, so throw an exception
        throw new System.ArgumentException("dVal doesn't equal sVal for the specified culture");
    }

    //Split the string on the separator 
    string[] segments = sVal.Split(sep);

    switch (segments.Length)
    {
        //Only one segment, so there was not fractional value - return 0
        case 1:
            return 0;
        //Two segments, so return the length of the second segment
        case 2:
            return segments[1].Length;

        //More than two segments means it's invalid, so throw an exception
        default:
            throw new Exception("Something bad happened!");
    }
}

And a shortcut method for US English:

public static int getDecimalCount(double dVal, string sVal)
{
    return getDecimalCount(dVal, sVal, "en-US");
}

Testing:

static void Main(string[] args)
{
    int i = 0;

    double d = 5900.00; 
    string s = "5900.00";
    Console.WriteLine("Testing with dVal = {0} and sVal = {1}.", d, s);
    i = getDecimalCount(d, s);
    Console.WriteLine("Expected output: 2. Actual output: {0}", i);
    Console.WriteLine();

    d = 5900.09;
    s = "5900.09";
    Console.WriteLine("Testing with dVal = {0} and sVal = {1}.", d, s);
    i = getDecimalCount(d, s);
    Console.WriteLine("Expected output: 2. Actual output: {0}", i);
    Console.WriteLine();

    d = 5900.000;
    s = "5900.000";
    Console.WriteLine("Testing with dVal = {0} and sVal = {1}.", d, s);
    i = getDecimalCount(d, s);
    Console.WriteLine("Expected output: 3. Actual output: {0}", i);
    Console.WriteLine();

    d = 5900.001;
    s = "5900.001";
    Console.WriteLine("Testing with dVal = {0} and sVal = {1}.", d, s);
    i = getDecimalCount(d, s);
    Console.WriteLine("Expected output: 3. Actual output: {0}", i);
    Console.WriteLine();

    d = 1.0; 
    s = "1.0";
    Console.WriteLine("Testing with dVal = {0} and sVal = {1}.", d, s);
    i = getDecimalCount(d, s);
    Console.WriteLine("Expected output: 1. Actual output: {0}", i);
    Console.WriteLine();

    d = 0.0000005; 
    s = "0.0000005";
    Console.WriteLine("Testing with dVal = {0} and sVal = {1}.", d, s);
    i = getDecimalCount(d, s);
    Console.WriteLine("Expected output: 7. Actual output: {0}", i);
    Console.WriteLine();

    d = 1.0000000; 
    s = "1.0000000";
    Console.WriteLine("Testing with dVal = {0} and sVal = {1}.", d, s);
    i = getDecimalCount(d, s);
    Console.WriteLine("Expected output: 7. Actual output: {0}", i);
    Console.WriteLine();

    d = 5900822; 
    s = "5900822";
    Console.WriteLine("Testing with dVal = {0} and sVal = {1}.", d, s);
    i = getDecimalCount(d, s);
    Console.WriteLine("Expected output: 0. Actual output: {0}", i);

    Console.ReadLine();
}

And finally, the output of the test:

Testing with dVal = 5900 and sVal = 5900.00. Expected output: 2. Actual output: 2

Testing with dVal = 5900.09 and sVal = 5900.09. Expected output: 2. Actual output: 2

Testing with dVal = 5900 and sVal = 5900.000. Expected output: 3. Actual output: 3

Testing with dVal = 5900.001 and sVal = 5900.001. Expected output: 3. Actual output: 3

Testing with dVal = 1 and sVal = 1.0. Expected output: 1. Actual output: 1

Testing with dVal = 5E-07 and sVal = 0.0000005. Expected output: 7. Actual output: 7

Testing with dVal = 1 and sVal = 1.0000000. Expected output: 7. Actual output: 7

Testing with dVal = 5900822 and sVal = 5900822. Expected output: 0. Actual output: 0

Let me know if you have questions about it or it doesn't make sense.

Rick Liddle
  • 2,684
  • 19
  • 31
  • You can see from the output above why we raised the issues we did early on. The .NET Framework doesn't treat the double value the same way it treats the string representation of a double. The first line of the output shows that: `5900` vs. `5900.00` – Rick Liddle Sep 08 '11 at 15:03
  • Thank's. That really a neat sample. However, it's really surprising me that .NET *CAN* give me a (double) 5900.00 (even to VS intellisense/quickwatch!) and we can't pick it up with .NET code.. – Independent Sep 09 '11 at 06:16
2

This is not what has been asked, but it is what I needed. Maybe it will help someone:

 private static int getDecimalCount(double val)
 {
     int i=0;
     while (Math.Round(val, i) != val)
         i++;
     return i;
 }
double val1 = 5900.00; int count1 = getDecimalCount(val1);//result: 0
double val2 = 5900.09; int count2 = getDecimalCount(val2);//result: 2
double val3 = 5900.000; int count3 = getDecimalCount(val3);//result: 0
double val4 = 1.0; int count4 = getDecimalCount(val4);//result: 0
double val5 = 0.0000005; int count5 = getDecimalCount(val5);//result: 7
double val6 = 1.0000000; int count6 = getDecimalCount(val6);//result: 0
double val7 = 5900822; int count7 = getDecimalCount(val7);//result: 0

double val9 = 4.5565d; int count9 = getDecimalCount(val9);//result: 4
Koray
  • 1,768
  • 1
  • 27
  • 37
2

You're thinking of the String representation of a double. A double doesn't make a difference between these for example:

123
123.0
123.000000

Thats all the same double, how many decimal places you get, really depends on your String formatting options (or which are set by default). What you can do, is format your doubles as Strings with a lot of decimal places, then strip all trailing zeros, and then count the decimal places.

Marian Theisen
  • 6,100
  • 29
  • 39
  • Thank's but sorry - strip all trailing zeros will just.. return 0? This format is really strong defined. The format is not arguable. If the double is 356.00, it must return 2. If the double is 356.09, it should also return 2. – Independent Sep 08 '11 at 10:43
  • if you parse 356.00 as a double, it will 'loose' the information about the .00, 356.00 is just a (decimal) string representation of a double value, so there is not really a point in determining the decimal places of 356.00, when it's a double. if you pass the argument as string, then you can count the decimals. but that makes no sense if you convert a double to a string, because every time you do that, you already specify (either explicitly, or implicitly via defaults or current culture) how many decimals you want. – Marian Theisen Sep 08 '11 at 10:55
  • I may be lost here, but it's of highest possible weight that the method returns 2 if the parameter passed the value of "356.00". That's, a part of my described problematic, why I need another solution then ToString, – Independent Sep 08 '11 at 10:59
  • The methods purpose is to answer the asker of "how many decimals do this (double)value have?". – Independent Sep 08 '11 at 11:02
  • I send a apologize to you, because you answered correct - but based on a error in my question. I was incorrectly asked for decimal places", while i meant ask for Decimals (no integers after the decimal place). However i'm still out of help after all this. – Independent Sep 08 '11 at 11:58
2

Create a method that receives a string instead of a double:

    public int CountDecimalDigits(string value)
    {
        char[] possibleChars = "0123456789.".ToCharArray();
        int decimalPoints = 0;
        foreach (char ch in value)
        {
            if (Array.IndexOf(possibleChars, ch) < 0)
                throw new Exception();
            if (ch == '.') decimalPoints++;
        }
        if (decimalPoints > 1)
            throw new Exception();
        if (decimalPoints == 0) return 0;
        return value.Length - value.IndexOf('.') - 1;
    }
Miguel Angelo
  • 23,796
  • 16
  • 59
  • 82