views:

178

answers:

8

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.

This is not homework!

+8  A: 

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

Henk Holterman
+3  A: 

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
A: 

I don't do c# but here's the algo

int decpos = str.indexof('.')
int numDec = str.length - decpos // number of decimal places
double prec = 1
while(numDec --> 0) prec *= 0.1
glowcoder
Will break for askers first example 4500, will give something like 0.00001
Binary Worrier
I would avoid use of the `-->` operator...
Gabe
@Gabe It's actually 2 operators --, and >. :-) @Binary true, special cases left as exercise to the reader.
glowcoder
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
@glowcoder: http://blogs.msdn.com/b/ericlippert/archive/2010/04/01/somelastminutefeatures.aspx is amusing, but let's leave such remarks for April fools. If you insist on a while loop I would use `numDec-- > 0` instead, but really I much prefer a for loop, which is designed for cases where your value is being incremented or decremented.
Brian
+1  A: 

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
Warning: First make sure the string is a valid number/the last character isn't a "."
luiscubal
+7  A: 

Just wondering if there is an easier solution to this.

No.

Use string:

string[] res = inputstring.Split('.');
int precision = res[1].Length;
codymanix
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
+4  A: 

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 internatialisation 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 a internal precission value. Therefore you could use Deciml.TryParse() and inspect the returned value. Maybe the parsing algorithm maintains the precission 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 precission issues. Finally finding the precission 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 precission issues...

UPDATE

Parsing into a deciaml 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.

Daniel Brückner
+1 For decimal.Precision, I had no idea it could do this :) See http://stackoverflow.com/questions/1132765/adjusting-decimal-precision-net
Binary Worrier
+2  A: 

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
See Daniel Bruckner's answer for how to access this information directly.
Brian
+1  A: 

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; http://stackoverflow.com/questions/763942/calculate-system-decimal-precision-and-scale which looks like it might be of interest if you wish to delve into this some more.

C.McAtackney
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
Cool, thanks for the tip.
C.McAtackney