5

I want with a given double number to return its "size" (known as |number|, or number of digit without the numbers after the dot), for example:

12.324654 -> 2
12 -> 2
0.99 -> 0
1.01 -> 1
901 -> 3
-0.99 -> 0
-9.999 -> 1

There must be a function is .net that I dont familiar with that does it..

Thanks.

Nir
  • 2,497
  • 9
  • 42
  • 71
  • 2
    Typically, |number| denotes the absolute value - which is not what you are after, obviously. – Mathias Jul 28 '11 at 20:09
  • The *integral* part of the number is to the left of the decimal point. The *fractional* part is to the right. So you are seeking a way to measure the number of decimal places used by the integral part. – Kirk Woll Jul 28 '11 at 20:13
  • |number| also known as a size of a group/number. It connect to what you are walking about.. – Nir Jul 28 '11 at 20:40

6 Answers6

13

Try log10(max(abs(number), 0.5)) + 1 rounded down.

Or, in actual C# syntax:

(int)(Math.Log10(Math.Max(Math.Abs(number), 0.5)) + 1)

OK, how does that work?

At first of all, p = log10(x) is the base-10 logarithm of x; that is to say, the value such that 10 raised to the p-th power (or 1 followed by p zeros) equals x. The logarithm essentially measures the length of the number, except that it's a smooth function of x:

Graph of the base-10 logarithm of x

(Note that, in languages that don't provide a base-10 logarithm function, we can always calculate it as log10(x) = log(x) / log(10), where log() is the logarithm function in any base.)

For example, we have

log10(1) = 0.0
log10(10) = 1.0
log10(100) = 2.0
log10(1000) = 3.0

but also e.g.:

log10(5) = 0.69897
log10(50) = 1.69897
log10(500) = 2.69897
log10(5000) = 3.69897

In general, n ≤ log10(x) < n+1 whenever 10nx < 10n+1.

Looking at the values above, it should be easy enough to see that, to get the number of base-10 digits in a whole number, we should round its base-10 logarithm down to the nearest whole number and add 1 (because we want the length of 10 to be 2, not 1).

However, there are a few more edge cases to consider:

First, the original questioner wanted the length of −x to equal the length of x. The logarithm is only defined for positive numbers, so we replace x by its absolute value to make it always positive.

Second, the original questioner also wanted the length of numbers between 0 and 1 to be zero. The logarithm, however, can take arbitrarily large negative values:

log10(0.1) = −1.0
log10(0.01) = −2.0
log10(0.001) = −3.0
log10(0.0001) = −4.0

and indeed, log10(0) = −∞. To satisfy this requirement, we simply make sure that the number whose length we calculate can never go below 0.5 by using the maximum of it and 0.5 as the input to log10(). (We could use any number between 0.1 and 1 as the cutoff, but 0.5 happens to be a nice round binary fraction.)

Also, we have to make sure to add +1 to the logarithm before rounding it, so that the number that we round is always non-negative. That's because (int) in C# actually rounds negative numbers up towards zero. For example, since log10(0.5) &approx; −0.3, the expression (int)Math.Log10(0.5) + 1 (rounding before addition) would evaluate to 0+1 = 1  rather than the expected 0.

Ilmari Karonen
  • 49,047
  • 9
  • 93
  • 153
  • How can you log a negative number? – Patrick Desjardins Jul 28 '11 at 20:08
  • @Daok: You can't, sorry. Corrected. – Ilmari Karonen Jul 28 '11 at 20:09
  • Can you explain it to me. I do not understand the logic behind but found it very wise + 1 – Patrick Desjardins Jul 28 '11 at 20:10
  • @Ilmari: How about input 0? And I don't know C#, but will the min() function force it to return 0? – zw324 Jul 28 '11 at 20:17
  • OK, I added an explanation, and also corrected the min() to max(). (Thanks, @Ziyao Wei!) – Ilmari Karonen Jul 28 '11 at 20:48
  • Thanks for the answer and the explanation!! I'll not use this because I know that in time, when I'll need to maintenance the code - this line wont make any sense to me.. Also my numbers arent that big, so currently the log function isnt must. – Nir Jul 29 '11 at 08:20
  • Well, you could always wrap it in a method and document what it does. (Ps. I tweaked the answer a bit more: I don't know if C#'s Math.Log10() likes 0 as an argument or not, but by moving the Max() inside it that issue never arises.) – Ilmari Karonen Jul 29 '11 at 11:02
  • Does your answer cost less/more then @Muad'Dib answer? – Nir Jul 29 '11 at 13:21
  • @nir: That's hard to say without running a benchmark. I'd hope mine would be faster, at least for large numbers, but it may ultimately come down to arbitrary differences in implementation overhead. Both should be fast enough for almost any practical purposes, anyway. – Ilmari Karonen Jul 29 '11 at 14:52
  • @nir (cont'd): I'm not really a C# programmer, but I went and ran a quick benchmark of the equivalent solutions in Perl just because I could: in that language, the character-counting solution is faster for numbers below 2^32, but slows down a lot above that point (while the log solution keeps running at about the same speed). As I noted, this is probably due to implementation details, so other languages and platforms are likely to be different. – Ilmari Karonen Jul 29 '11 at 14:58
  • Hi, @DavideCannizzo. Just out of curiosity, is there a particular reason why your added en spaces around (some of) the math expressions above? I'm not convinced they really help readability (especially for single variables or in expressions like "the *p*-th power"), but I figured I'd ask before just outright reverting your edit, just in case there's something I'm missing here. – Ilmari Karonen Jan 05 '18 at 10:09
  • Expressions have got some internal space (such as those between the operators), so I believe they are more tidied with a quite larger space at the beginning and the end of them. – Davide Cannizzo Jan 05 '18 at 11:52
6
((int)Math.Abs(12.324654)).ToString().Length

Math.Abs will convert to positive number (don't want to count the negative sign)
(int) will drop the digits following the decimal point
ToString() converts to a string ===> "12"
Length tells you how many characters there are
there is an edge case where (int)( some number ) == 0, where the length is going to give you 1-- you may not want this, so be aware of the possibility

now, what you COULD do is make this an extention method....

public static class Utils
{
   public static int Size(this double n)
   {
     int i = (int)Math.Abs(n);
     if ( i == 0 ) return 0;
     return  i.ToString().Length;
   }
}

12.324654.Size() == 2;
Muad'Dib
  • 28,542
  • 5
  • 55
  • 68
  • Pretty straight forward :) +1 I like the simplicity. – Patrick Desjardins Jul 28 '11 at 20:13
  • Doesn't account for negative numbers. You'd need the absolute value, otherwise the requirement in the original question (-9.999 -> 1) wouldn't work. Maybe ((int)(Math.Abs(12.324654)).ToString().Length; ? Otherwise, I like the simplicity of this approach. – ZombieSheep Jul 28 '11 at 20:13
  • :) Now it's been updated, I'll delete my answer. :) I think it needs to account for the possibility of the result being 0 though, since the OP wanted 0 to return a zero length... – ZombieSheep Jul 28 '11 at 20:17
  • 1
    0.99 -> 0 will also return 1, isn't it? – hungryMind Jul 28 '11 at 20:18
  • @hungryMind, "(int)(someDouble)" will drop the part after the decimal point, so (int)0.99 == 1 (because of the 0) --- changed the code to fix this – Muad'Dib Jul 28 '11 at 20:20
  • Thats what I am saying, 0.99 expects 0 as result, but ur formula will return 1. This one will work ok, ((int)Math.Abs(yourNumber *10)).ToString().Length - 1 – hungryMind Jul 28 '11 at 20:22
  • @hungryMind, you are right. I added comments about the edge case. thanks! – Muad'Dib Jul 28 '11 at 20:27
  • @muad what about my solution? isn't it straight forwrd – hungryMind Jul 28 '11 at 20:34
2

You could do something like below to account for your 0 digits returning zero length

private static int CountAbsoluteDigits(double p) {
    int absolute = (int)Math.Abs(p);
    if (0 == absolute)
        return 0;
    else
        return absolute.ToString().Length;
}

...

var length = CountAbsoluteDigits(12.324654);

This is the output I got with the numbers you supplied..

12.324654 -> 2
12 -> 2
0.99 -> 0
1.01 -> 1
901 -> 3
-0.99 -> 0
-9.999 -> 1

Quintin Robinson
  • 81,193
  • 14
  • 123
  • 132
1

Stealing Quintin's format:

private static int CountAbsoluteDigits(double p) {
    int ip = (int)p;
    int answer = 0;
    while (ip!=0) {
        answer++;
        ip/=10;
    }
    return answer;
}

No trick (e.g., abs(), if) required in this.

zw324
  • 26,764
  • 16
  • 85
  • 118
1

Courtsey Muad'Dib

int GetLength(double value){
    return ((int)Math.Abs(value*10d)).ToString().Length - 1
}
hungryMind
  • 6,931
  • 4
  • 29
  • 45
0

Converting to integer and converting the result to a string will only work if the value is within the range of an int. If you need values above 2 billion, you'll either need to work with logs, or deal convert the double to a string.

static int GetLength(double d)
{
    d = Math.Abs(d);
    if (d < 1.0) return 0;
    if (double.IsInfinity(d)) return int.MaxValue;
    if (double.IsNaN(d)) return 0;
    return (int)Math.Floor(Math.Log10(d)) + 1;
}

static int GetLength2(double d)
{
    d = Math.Abs(d);
    if (d < 1.0) return 0;
    if (double.IsInfinity(d)) return int.MaxValue;
    if (double.IsNaN(d)) return 0;
    string s = d.ToString("E"); // returns a string in the format "1.012435E+001"
    return int.Parse(s.Substring(s.Length - 3)) + 1;
}

static void Test(double d) { Debug.WriteLine(d + " -> " + GetLength(d) + ", " + GetLength2(d)); }

static void Main(string[] args)
{
    Test(0);
    Test(0.125);
    Test(0.25);
    Test(0.5);
    Test(1);
    Test(2);
    Test(10);
    Test(10.5);
    Test(10.25);
    Test(10.1243542354235623542345234523452354);
    Test(999999);
    Test(1000000);
    Test(1000001);
    Test(999999.111);
    Test(1000000.111);
    Test(1000001.111);
    Test(double.MaxValue);
    Test(double.MinValue);
    Test(double.PositiveInfinity);
    Test(double.NegativeInfinity);
    Test(double.NaN);
    Test(double.Epsilon);
}

Results:

0 -> 0, 0
0.125 -> 0, 0
0.25 -> 0, 0
0.5 -> 0, 0
1 -> 1, 1
2 -> 1, 1
10 -> 2, 2
10.5 -> 2, 2
10.25 -> 2, 2
10.1243542354236 -> 2, 2
999999 -> 6, 6
1000000 -> 7, 7
1000001 -> 7, 7
999999.111 -> 6, 6
1000000.111 -> 7, 7
1000001.111 -> 7, 7
1.79769313486232E+308 -> 309, 309
-1.79769313486232E+308 -> 309, 309
Infinity -> 2147483647, 2147483647
-Infinity -> 2147483647, 2147483647
NaN -> 0, 0
4.94065645841247E-324 -> 0, 0
David Yaw
  • 27,383
  • 4
  • 60
  • 93