Get the number of digits to a decimal point

I have a variable of type decimal , and I want to check the number of digits to the decimal point in it. What should I do? For example, 467.45 should return 3 .

+61
decimal c # numbers
Feb 04 '14 at 7:51
source share
26 answers

A solution without conversion to string (which can be dangerous in the case of exotic crops):

 static int GetNumberOfDigits(decimal d) { decimal abs = Math.Abs(d); return abs < 1 ? 0 : (int)(Math.Log10(decimal.ToDouble(abs)) + 1); } 

Please note that this solution is valid for all decimal values.

UPDATE

In fact, this solution does not work with some large values, for example: 999999999999998 , 999999999999999 , 9999999999999939 ...

Obviously, the mathematical operations with double not accurate enough for this task.

When looking for incorrect values, I tend to use string based alternatives suggested in this section. As for me, this indicates that they are more reliable and easier to use (but keep in mind the cultures). Loop-based solutions can be faster though.

Thanks to the commentators, ashamed of me, a lesson for you.

+80
Feb 04 '14 at 7:59
source share

Instead of converting to a string, you can also divide the number by 10 until it becomes 0. Itโ€™s interesting that mathematical operations on decimals are much slower than converting a decimal string to a string and returning the length (see ..
This solution does not use mathematical methods that take double entry; therefore, all operations are performed with decimal places , and casting is not involved.

 using System; public class Test { public static void Main() { decimal dec = -12345678912345678912345678912.456m; int digits = GetDigits(dec); Console.WriteLine(digits.ToString()); } static int GetDigits(decimal dec) { decimal d = decimal.Floor(dec < 0 ? decimal.Negate(dec) : dec); // As stated in the comments of the question, // 0.xyz should return 0, therefore a special case if (d == 0m) return 0; int cnt = 1; while ((d = decimal.Floor(d / 10m)) != 0m) cnt++; return cnt; } } 

Conclusion 29 . To run this sample, go to the page.




Side note: some tests show amazing results (10,000 runs):

  • while ((d = decimal.Floor(d / 10m)) != 0m) : 25ms
  • while ((d = d / 10m) > 1m) : 32 ms
  • ToString with Math-double-operations: 3ms
  • Decimal ToString: 3ms
  • BigInt (see @Heinzi answer ): 2ms

Also, using random numbers, and not always the same value (to avoid possible caching of decimal string to string conversion), it was shown that string methods are much faster.

+32
04 Feb '14 at 8:01
source share

I would try this:

 Math.Truncate(467.45).ToString().Length 

If you want to be sure that you do not have any strange results for different cultures and with negative decimal places, you better use this:

 var myDecimal = 467.45m; Math.Truncate(Math.Abs(myDecimal)).ToString(CultureInfo.InvariantCulture).Length 
+24
Feb 04 '14 at 7:55
source share

I would prefer the following instead of executing an int to ensure that you can also process large numbers (e.g. decimal.MaxValue ):

 Math.Truncate ( Math.Abs ( decValue ) ).ToString( "####" ).Length 
+13
Feb 04 '14 at 7:58
source share

Here's a recursive example (mostly for fun).

 void Main() { digitCount(0.123M); //0 digitCount(493854289.543354345M); //10 digitCount(4937854345454545435549.543354345M); //22 digitCount(-4937854345454545435549.543354345M); //22 digitCount(1.0M); //1 //approximately the biggest number you can pass to the function that works. digitCount(Decimal.MaxValue + 0.4999999M); //29 } int digitCount(decimal num, int count = 0) { //divided down to last digit, return how many times that happened if(Math.Abs(num) < 1) return count; return digitCount(num/10, ++count); //increment the count and divide by 10 to 'remove' a digit } 
+7
Feb 04 '14 at 17:24
source share
 decimal d = 467.45M; int i = (int)d; Console.WriteLine(i.ToString(CultureInfo.InvariantCulture).Length); //3 

As a method;

 public static int GetDigitsLength(decimal d) { int i = int(d); return i.ToString(CultureInfo.InvariantCulture).Length; } 

Note Of course, you must first check that your decimal point is greater than Int32.MaxValue or not, because if that is the case, you get an OverflowException .

Whether such a case using long instead of int might be better. However, even long ( System.Int64 ) is not large enough to preserve all possible decimal values.

As stated in Rawling, your full part may contain a thousands separator, and my code will be broken in that case. Since this way it completely ignores my number, it contains NumberFormatInfo.NumberGroupSeparator or not.

That's why getting numbers is the best approach. How;

 i.ToString().Where(c => Char.IsDigit(c)).ToArray() 
+6
Feb 04 '14 at 7:55
source share

Math.Floor(Math.Log10((double)n) + 1); - way.

Converting to int is equal to BAD, because decimal may be larger than int :

 Decimal.MaxValue = 79,228,162,514,264,337,593,543,950,335; Int32.MaxValue = 2,147,483,647; //that is, hexadecimal 0x7FFFFFFF; 

Math.Floor(n).ToString().Count(); bad because it can include thousands of delimiters.

+5
Feb 04 '14 at 8:00
source share
 var sep = Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator); var count = d.ToString().TakeWhile(c => c != sep).Count(); 
+5
Feb 04 '14 at 8:25
source share

If you have a bias towards smaller numbers, you can use something simpler like this.

It breaks down into two methods, so the first method is smaller and can be built in.

The performance is about the same as the solution with Log10, but without rounding errors. The method using Log10 is still the fastest (bit) specifically for numbers> 1 million.

  public static int CountNrOfDigitsIfs(decimal d) { var absD = Math.Abs(d); // 1 if (absD < 10M) return 1; // 2 if (absD < 100M) return 2; // 3 if (absD < 1000M) return 3; // 4 if (absD < 10000M) return 4; return CountNrOfDigitsIfsLarge(d); } private static int CountNrOfDigitsIfsLarge(decimal d) { // 5 if (d < 100000M) return 5; // 6 if (d < 1000000M) return 6; // 7 if (d < 10000000M) return 7; // 8 if (d < 100000000M) return 8; // 9 if (d < 1000000000M) return 9; // 10 if (d < 10000000000M) return 10; // 11 if (d < 100000000000M) return 11; // 12 if (d < 1000000000000M) return 12; // 13 if (d < 10000000000000M) return 13; // 14 if (d < 100000000000000M) return 14; // 15 if (d < 1000000000000000M) return 15; // 16 if (d < 10000000000000000M) return 16; // 17 if (d < 100000000000000000M) return 17; // 18 if (d < 1000000000000000000M) return 18; // 19 if (d < 10000000000000000000M) return 19; // 20 if (d < 100000000000000000000M) return 20; // 21 if (d < 1000000000000000000000M) return 21; // 22 if (d < 10000000000000000000000M) return 22; // 23 if (d < 100000000000000000000000M) return 23; // 24 if (d < 1000000000000000000000000M) return 24; // 25 if (d < 10000000000000000000000000M) return 25; // 26 if (d < 100000000000000000000000000M) return 26; // 27 if (d < 1000000000000000000000000000M) return 27; // 28 if (d < 10000000000000000000000000000M) return 28; return 29; // Max nr of digits in decimal } 

This code is generated using the following T4 pattern:

 <# const int SIGNIFICANT_DECIMALS = 29; const int SPLIT = 5; #> namespace Study.NrOfDigits { static partial class DigitCounter { public static int CountNrOfDigitsIfs(decimal d) { var absD = Math.Abs(d); <# for (int i = 1; i < SPLIT; i++) { // Only 29 significant digits var zeroes = new String('0', i); #> // <#= i #> if (absD < 1<#= zeroes #>M) return <#= i #>; <# } #> return CountNrOfDigitsIfsLarge(d); } private static int CountNrOfDigitsIfsLarge(decimal d) { <# for (int i = SPLIT; i < SIGNIFICANT_DECIMALS; i++) { // Only 29 significant digits var zeroes = new String('0', i); #> // <#= i #> if (d < 1<#= zeroes #>M) return <#= i #>; <# } #> return <#= SIGNIFICANT_DECIMALS #>; // Max nr of digits in decimal } } } 
+5
Feb 05
source share

This will be done if you really do not want to use the log method (most suitable for IMO). This is the clearest way to do this using ToString ():

 Math.Abs(val).ToString("f0", CultureInfo.InvariantCulture).Length 

Or, conversely, if you do not want to consider 0.123M as having one digit:

 Math.Abs(val).ToString("#", CultureInfo.InvariantCulture).Length 
+4
Feb 04 '14 at 11:14
source share

You can use the ToString function with a custom format.

 Decimal value = 467.45m; int count = Math.Abs(value).ToString("#", System.Globalization.CultureInfo.InvariantCulture).Length; 

# indicates that you only need the characters before .

System.Globalization.CultureInfo.InvariantCulture ensures that you do not get any formatting from the Region option.

+4
Feb 04 '14 at 14:50
source share

TL; DR all other answers. I wrote this in PHP and the math would be the same. (If I knew C #, I would write in that language.)

 $input=21689584.999; $input=abs($input); $exp=0; do{ $test=pow(10,$exp); if($test > $input){ $digits=$exp; } if($test == $input){ $digits=$exp+1; } $exp++; }while(!$digits); if($input < 1){$digits=0;} echo $digits; 

I have no doubt that there is a better way, but I wanted to throw $ .02

EDIT:

I have php-ized code that I mentioned in my comments, but ended up converting int.

 function digitCount($input){ $digits=0; $input=abs($input); while ($input >= 1) { $digits++; $input=$input/10; //echo $input."<br>"; } return $digits; } $big=(float)(PHP_INT_MAX * 1.1); echo digitCount($big); 
+3
04 Feb '14 at
source share

The mathematical way to do this (and probably the fastest) is to get the base 10 logarithm of the absolute value of that number and round it up.

 Math.Floor(Math.Log10(Math.Abs(val)) + 1); 
+3
04 Feb '14 at 15:17
source share

So, I came across this before and solved it using this code:

 SqlDecimal d = new SqlDecimal(467.45M); int digits = d.Precision - d.Scale; 

SqlDecimal is part of the System.Data.SqlTypes namespace. Accuracy is the total number of significant digits, and Scale is the number of digits after the decimal point.

Now I know that one objection to this route is that SqlDecimal is part of SQL Server-specific code. This is a valid point, but I would also note that it is part of the .NET platform itself, and since then, at least with version 1.1, so it seems that it will continue to be applied no matter what the code around it does .

I looked under the hood with the decompiler ( "dotPeek" JetBrains in this case) to see maybe the code for calculating the accuracy and scale can be easily extracted and simply used without retracting SqlDecimal . The code for calculating the scale is very simple, but the accuracy calculation method is not trivial, so if it were me, I would go through SqlDecimal .

+3
Feb 05 '14 at 6:10
source share

It will be a Java solution.

 public class test { public static void main(String args[]) { float f = 1.123f; int a = (int) f; int digits = 0; while (a > 0) { digits++; a=a/10; } System.out.println("No Of digits before decimal="+digits); } } 
+2
Feb 04 '14 at 14:13
source share

If you process zeros or the absence of zeros as a 1 number, this is normal. If you want zero to return zero or zero to return zero, then there are a few edge cases that should not be too complicated to add. In addition, Absolute must handle negative numbers. Added this test case.

  const decimal d = 123.45m; const decimal d1 = 0.123m; const decimal d2 = .567m; const decimal d3 = .333m; const decimal d4 = -123.45m; NumberFormatInfo currentProvider = NumberFormatInfo.InvariantInfo; var newProvider = (NumberFormatInfo) currentProvider.Clone(); newProvider.NumberDecimalDigits = 0; string number = d.ToString("N", newProvider); //returns 123 = .Length = 3 string number1 = d1.ToString("N", newProvider); //returns 0 = .Length = 1 string number2 = d2.ToString("N", newProvider); //returns 1 = .Length = 1 string number3 = d3.ToString("N", newProvider); //returns 0 = .Length = 1 string number4 = Math.Abs(d4).ToString("N", newProvider); //returns 123 = .Length = 3 

Here is a somewhat final solution, if you find a test case that doesn't work, let me know. He should return 3,0,0,0,3 for the provided test cases.

  public static int NumbersInFrontOfDecimal(decimal input) { NumberFormatInfo currentProvider = NumberFormatInfo.InvariantInfo; var newProvider = (NumberFormatInfo)currentProvider.Clone(); newProvider.NumberDecimalDigits = 0; var absInput = Math.Abs(input); var numbers = absInput.ToString("N", newProvider); //Handle Zero and < 1 if (numbers.Length == 1 && input < 1.0m) { return 0; } return numbers.Length; } 
+2
Feb 04 '14 at 18:40
source share

This answer is pretty much removed from Calculate System.Decimal Precision and Scale , but with a slight change to fit the question asked.

 class Program { static void Main() { decimal dec = 467.45m; Console.WriteLine(dec.GetNumberOfDigitsBeforeDecimalPlace()); } } public static class DecimalEx { public static int GetNumberOfDigitsBeforeDecimalPlace(this decimal dec) { var x = new System.Data.SqlTypes.SqlDecimal(dec); return x.Precision - x.Scale; } } 

Also, if you want to do this without using the SqlDecimal class, check out Jon Skeet's answer to the same question.

+2
Feb 05 '14 at 8:52
source share

Use modulo, I'm not a C # programmer, but I'm sure this solution works:

 double i = 1; int numberOfDecimals = 0; while (varDouble % i != varDouble) { numberOfDecimals++; i*=10; } return numberOfDecimals; 
+2
Feb 05 '14 at 14:29
source share

Other solutions will lose numbers if the number is too large.

 public int Digits(Decimal i) { NumberFormatInfo format = CultureInfo.CurrentCulture.NumberFormat; var str = Math.Abs(i).ToString().Replace(format.NumberGroupSeparator, ""); var index = str.IndexOf(format.NumberDecimalSeparator); var digits = index == -1 ? str.Length : index; } 
+1
04 Feb '14 at 7:57
source share

Here is my optimized version of the code inspired by Gray:

  static int GetNumOfDigits(decimal dTest) { int nAnswer = 0; dTest = Math.Abs(dTest); //For loop version for (nAnswer = 0; nAnswer < 29 && dTest > 1; ++nAnswer) { dTest *= 0.1M; } //While loop version //while (dTest > 1) //{ // nAnswer++; // dTest *= 0.1M; //} return (nAnswer); } 

If you do not want Math.Abs โ€‹โ€‹to be called inside this function, be sure to use it outside the function above the parameter before calling GetNumOfDigits.

I decided to remove other codes in order to reduce the clutter in my answer, although they helped me get to this point ...

If there are any improvements, let me know and I will update it :).

+1
Feb 04 '14 at 19:51
source share

To get an accurate and culturally agnostic response, I do the following:

  • Use System.Numerics.BigInteger , whose constructor takes a decimal number and does not seem to produce rounding errors.
  • Use BigInteger.Abs() to remove any character.
  • Use BigInteger.ToString() with the "#" format to suppress any delimiters that may occur.

the code

 decimal num = 123213123.123123M; int length = BigInteger.Abs((BigInteger)num).ToString("#").Length; 
+1
Feb 05 '14 at 18:09
source share

You can do this by rounding the number, then getting the length of the new number. You can do it as follows:

 var number = 476.43; var newNumber = Math.round(number); //get the length of the rounded number, and subtract 1 if the //number is negative (remove the negative sign from the count) int digits = newNumber.ToString().Length - (number < 0 ? 1 : 0); 
+1
Feb 06 '14 at 0:24
source share

simply:

 string value = "467.45"; int count = value.split('.')[0] == "0" ? 0 : value.split('.')[0].ToString().Replace("-","").Count(); 
0
04 Feb '14 at 15:47
source share

Algorithm:

  • Convert |decimal| to a string.
  • If "." exists in decimal, cut in front of it, otherwise consider the whole number.
  • The length of the return string.

Example:

 3.14 --> 3.14 --> "3.14" --> "3.14".Substring(0,1) --> "3".Length --> 1 -1024 --> 1024 --> "1024" --> IndexOf(".") = -1 --> "1024" --> 4 

The code:

 static int getNumOfDigits (decimal num) { string d = Math.Abs(num).ToString(); if (d.IndexOf(".") > -1) { d = d.Substring(0, d.IndexOf(".")); } return d.Length; } 
0
Feb 05 '14 at 7:27
source share

I did not test this, but I would save it directly and do:

 decimal value = 467.45; string str = Convert.toString(value); // convert your decimal type to a string string[] splitStr = str.split('.'); // split it into an array (use comma separator assuming you know your cultural context) Console.WriteLine(splitStr[0].Count); // get the first element. You can also get the number of figures after the point by indexing the next value in the array. 

This does not handle negative numbers. If you care about those who think about absolute meaning. Also, if you want 0 to decimal not counted, you can use a simple if statement to test it.

0
Feb 05 '14 at 8:58
source share

Using:

 var value=467.45; var length=((int)value).ToString().Length 
-one
Feb 04 '14 at 8:02
source share



All Articles