13

We have an interesting problem were we need to determine the decimal precision of a users input (textbox). Essentially we need to know the number of decimal places entered and then return a precision number, this is best illustrated with examples:

4500 entered will yield a result 1
4500.1 entered will yield a result 0.1
4500.00 entered will yield a result 0.01
4500.450 entered will yield a result 0.001

We are thinking to work with the string, finding the decimal separator and then calculating the result. Just wondering if there is an easier solution to this.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Quinten
  • 131
  • 1
  • 1
  • 3
  • 3
    Are you sure 4500 would be a 1, and not a 100? – corsiKa Jul 19 '10 at 14:59
  • What is expected result in case 0.0 and 0.1 ? – user1785960 Jun 06 '16 at 08:43
  • For Me it is confusing question. You do not ask for decimal precision. You ask: how to get the number of digits in a fraction part. Definition for 'decimal precision' is here for Me: https://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#i16209 – user1785960 Jun 06 '16 at 08:58

7 Answers7

21

I think you should just do what you suggested - use the position of the decimal point. Obvious drawback might be that you have to think about internationalization yourself.

var decimalSeparator = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;

var position = input.IndexOf(decimalSeparator);

var precision = (position == -1) ? 0 : input.Length - position - 1;

// This may be quite unprecise.
var result = Math.Pow(0.1, precision);

There is another thing you could try - the Decimal type stores an internal precision value. Therefore you could use Decimal.TryParse() and inspect the returned value. Maybe the parsing algorithm maintains the precision of the input.

Finally I would suggest not to try something using floating point numbers. Just parsing the input will remove any information about trailing zeros. So you have to add an artifical non-zero digit to preserve them or do similar tricks. You might run into precision issues. Finally finding the precision based on a floating point number is not simple, too. I see some ugly math or a loop multiplying with ten every iteration until there is no longer any fractional part. And the loop comes with new precision issues...

UPDATE

Parsing into a decimal works. Se Decimal.GetBits() for details.

var input = "123.4560";

var number = Decimal.Parse(input);

// Will be 4.
var precision = (Decimal.GetBits(number)[3] >> 16) & 0x000000FF;

From here using Math.Pow(0.1, precision) is straight forward.

UPDATE 2

Using decimal.GetBits() will allocate an int[] array. If you want to avoid the allocation you can use the following helper method which uses an explicit layout struct to get the scale directly out of the decimal value:

static int GetScale(decimal d)
{
    return new DecimalScale(d).Scale;
}

[StructLayout(LayoutKind.Explicit)]
struct DecimalScale
{
    public DecimalScale(decimal value)
    {
        this = default;
        this.d = value;
    }

    [FieldOffset(0)]
    decimal d;

    [FieldOffset(0)]
    int flags;

    public int Scale => (flags >> 16) & 0xff;
}
MarkPflug
  • 28,292
  • 8
  • 46
  • 54
Daniel Brückner
  • 59,031
  • 16
  • 99
  • 143
11

Just wondering if there is an easier solution to this.

No.

Use string:

string[] res = inputstring.Split('.');
int precision = res[1].Length;
codymanix
  • 28,510
  • 21
  • 92
  • 151
  • 16
    The character to split on needs to be the correct decimal separator - [NumberFormatInfo.NumberDecimalSeparator](http://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo.numberdecimalseparator.aspx) – ChrisF Jul 19 '10 at 14:27
9

Since your last examples indicate that trailing zeroes are significant, I would rule out any numerical solution and go for the string operations.

H H
  • 263,252
  • 30
  • 330
  • 514
3

No, there is no easier solution, you have to examine the string. If you convert "4500" and "4500.00" to numbers, they both become the value 4500 so you can't tell how many non-value digits there were behind the decimal separator.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
2

As an interesting aside, the Decimal tries to maintain the precision entered by the user. For example,

Console.WriteLine(5.0m);
Console.WriteLine(5.00m);
Console.WriteLine(Decimal.Parse("5.0"));
Console.WriteLine(Decimal.Parse("5.00"));

Has output of:

5.0
5.00
5.0
5.00

If your motivation in tracking the precision of the input is purely for input and output reasons, this may be sufficient to address your problem.

Brian
  • 25,523
  • 18
  • 82
  • 173
1

Working with the string is easy enough.

If there is no "." in the string, return 1.

Else return "0.", followed by n-1 "0", followed by one "1", where n is the length of the string after the decimal point.

Amnon
  • 7,652
  • 2
  • 26
  • 34
1

Here's a possible solution using strings;

static double GetPrecision(string s)
{ 
    string[] splitNumber = s.Split('.');
    if (splitNumber.Length > 1)
    {
        return 1 / Math.Pow(10, splitNumber[1].Length);
    }
    else
    {
        return 1;
    }
}

There is a question here; Calculate System.Decimal Precision and Scale which looks like it might be of interest if you wish to delve into this some more.

Community
  • 1
  • 1
Chris McAtackney
  • 5,192
  • 8
  • 45
  • 69
  • 1
    You need to use [NumberFormatInfo.NumberDecimalSeparator](http://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo.numberdecimalseparator.aspx) to get the correct decimal separator for the locale. – ChrisF Jul 19 '10 at 15:09