找到小数位数,无论文化如何

时间:2022-03-29 16:31:07

I'm wondering if there is a concise and accurate way to pull out the number of decimal places in a decimal value (as an int) that will be safe to use across different culture info?

我想知道是否有一种简洁而准确的方法,可以在一个十进制值(作为int)中提取小数位数,在不同的文化信息中安全地使用?

For example:
19.0 should return 1,
27.5999 should return 4,
19.12 should return 2,
etc.

例如:19.0应该返回1,27.5999应该返回4,19.12应该返回2,等等。

I wrote a query that did a string split on a period to find decimal places:

我写了一个查询,用一个句点分割一个字符串来查找小数点:

int priceDecimalPlaces = price.ToString().Split('.').Count() > 1 
                  ? price.ToString().Split('.').ToList().ElementAt(1).Length 
                  : 0;

But it occurs to me that this will only work in regions that use the '.' as a decimal separator and is therefore very brittle across different systems.

但我突然想到,这只适用于使用“。作为十进制分离器,因此在不同的系统中非常脆弱。

13 个解决方案

#1


118  

I used Joe's way to solve this issue :)

我用乔的方式来解决这个问题:

decimal argument = 123.456m;
int count = BitConverter.GetBytes(decimal.GetBits(argument)[3])[2];

#2


16  

Since none of the answers supplied were good enough for the magic number "-0.01f" converted to decimal.. i.e: GetDecimal((decimal)-0.01f);
I can only assume a colossal mind-fart virus attacked everyone 3 years ago :)
Here is what seems to be a working implementation to this evil and monstrous problem, the very complicated problem of counting the decimal places after the point - no strings, no cultures, no need to count the bits and no need to read math forums.. just simple 3rd grade math.

由于所提供的答案中没有一个是足够的好到可以将“-0.01f”转换成十进制。我。艾凡:GetDecimal((十进制)-0.01 f);我只能承担一个巨大的mind-fart病毒攻击每个人3年前:)这是什么似乎是一个工作实现这邪恶和巨大的问题,很复杂的计算问题点后的小数点后——无附带条件的,没有文化,不需要计算部分和不需要阅读数学论坛. .只是简单的三年级数学。

public static class MathDecimals
{
    public static int GetDecimalPlaces(decimal n)
    {
        n = Math.Abs(n); //make sure it is positive.
        n -= (int)n;     //remove the integer part of the number.
        var decimalPlaces = 0;
        while (n > 0)
        {
            decimalPlaces++;
            n *= 10;
            n -= (int)n;
        }
        return decimalPlaces;
    }
}

private static void Main(string[] args)
{
    Console.WriteLine(1/3m); //this is 0.3333333333333333333333333333
    Console.WriteLine(1/3f); //this is 0.3333333

    Console.WriteLine(MathDecimals.GetDecimalPlaces(0.0m));                  //0
    Console.WriteLine(MathDecimals.GetDecimalPlaces(1/3m));                  //28
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)(1 / 3f)));     //7
    Console.WriteLine(MathDecimals.GetDecimalPlaces(-1.123m));               //3
    Console.WriteLine(MathDecimals.GetDecimalPlaces(43.12345m));             //5
    Console.WriteLine(MathDecimals.GetDecimalPlaces(0));                     //0
    Console.WriteLine(MathDecimals.GetDecimalPlaces(0.01m));                 //2
    Console.WriteLine(MathDecimals.GetDecimalPlaces(-0.001m));               //3
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)-0.00000001f)); //8
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)0.0001234f));   //7
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)0.01f));        //2
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)-0.01f));       //2
}

#3


15  

I'd probably use the solution in @fixagon's answer.

我可能会在@fixagon的答案中使用这个解决方案。

However, while the Decimal struct doesn't have a method to get the number of decimals, you could call Decimal.GetBits to extract the binary representation, then use the integer value and scale to compute the number of decimals.

然而,尽管Decimal struct没有方法来获取小数,但是可以调用Decimal。GetBits提取二进制表示,然后使用整数值和缩放来计算小数的数量。

This would probably be faster than formatting as a string, though you'd have to be processing an awful lot of decimals to notice the difference.

这可能比字符串格式要快,尽管您需要处理大量小数才能注意到差异。

I'll leave the implementation as an exercise.

我将把实现留作练习。

#4


13  

One of the best solutions for finding the number of digits after the decimal point is shown in burning_LEGION's post.

在burning_LEGION的文章中显示了找到小数点后数字的最好的解决方案之一。

Here I am using parts from a STSdb forum article: Number of digits after decimal point.

这里我使用的部分来自STSdb论坛文章:小数点后的位数。

In MSDN we can read the following explanation:

在MSDN中,我们可以看到以下解释:

"A decimal number is a floating-point value that consists of a sign, a numeric value where each digit in the value ranges from 0 to 9, and a scaling factor that indicates the position of a floating decimal point that separates the integral and fractional parts of the numeric value."

“十进制数是一个浮点值,它包含一个符号、一个数值,该数值中的每个数字都在0到9之间,以及一个缩放因子,该因子指示一个浮点数的位置,该浮点数分隔数值的整数部分和小数部分。”

And also:

还有:

"The binary representation of a Decimal value consists of a 1-bit sign, a 96-bit integer number, and a scaling factor used to divide the 96-bit integer and specify what portion of it is a decimal fraction. The scaling factor is implicitly the number 10, raised to an exponent ranging from 0 to 28."

十进制值的二进制表示由一个1位符号、一个96位整数和一个缩放因子组成,该因子用于除96位整数并指定它的哪个部分是十进制分数。比例因子是10的隐含值,它被提升到从0到28的指数。

On internal level the decimal value is represented by four integer values.

在内部级别上,十进制值由四个整数值表示。

找到小数位数,无论文化如何

There is a publicly available GetBits function for getting the internal representation. The function returns an int[] array:

有一个公共可用的GetBits函数来获取内部表示。函数返回一个int[]数组:

[__DynamicallyInvokable] 
public static int[] GetBits(decimal d)
{
    return new int[] { d.lo, d.mid, d.hi, d.flags };
}

The fourth element of the returned array contains a scale factor and a sign. And as the MSDN says the scaling factor is implicitly the number 10, raised to an exponent ranging from 0 to 28. This is exactly what we need.

返回数组的第四个元素包含比例因子和符号。正如MSDN所说的比例因子隐含地是数字10,指数从0到28。这正是我们所需要的。

Thus, based on all above investigations we can construct our method:

因此,基于以上研究,我们可以构建我们的方法:

private const int SIGN_MASK = ~Int32.MinValue;

public static int GetDigits4(decimal value)
{
    return (Decimal.GetBits(value)[3] & SIGN_MASK) >> 16;
}

Here a SIGN_MASK is used to ignore the sign. After logical and we have also shifted the result with 16 bits to the right to receive the actual scale factor. This value, finally, indicates the number of digits after the decimal point.

这里使用SIGN_MASK来忽略符号。在符合逻辑之后,我们还将16位的结果向右移动,以接收实际的比例因子。这个值最后表示小数点后的位数。

Note that here MSDN also says the scaling factor also preserves any trailing zeros in a Decimal number. Trailing zeros do not affect the value of a Decimal number in arithmetic or comparison operations. However, trailing zeros might be revealed by the ToString method if an appropriate format string is applied.

请注意,这里MSDN还说缩放因子也将所有尾随的0保留为小数。在算术或比较操作中,尾零不影响十进制数的值。但是,如果应用了适当的格式字符串,则ToString方法可能会显示尾随零。

This solutions looks like the best one, but wait, there is more. By accessing private methods in C# we can use expressions to build a direct access to the flags field and avoid constructing the int array:

这个解看起来是最好的,但是等等,还有更多。通过访问c#中的私有方法,我们可以使用表达式来构建对flags字段的直接访问,并避免构造int数组:

public delegate int GetDigitsDelegate(ref Decimal value);

public class DecimalHelper
{
    public static readonly DecimalHelper Instance = new DecimalHelper();

    public readonly GetDigitsDelegate GetDigits;
    public readonly Expression<GetDigitsDelegate> GetDigitsLambda;

    public DecimalHelper()
    {
        GetDigitsLambda = CreateGetDigitsMethod();
        GetDigits = GetDigitsLambda.Compile();
    }

    private Expression<GetDigitsDelegate> CreateGetDigitsMethod()
    {
        var value = Expression.Parameter(typeof(Decimal).MakeByRefType(), "value");

        var digits = Expression.RightShift(
            Expression.And(Expression.Field(value, "flags"), Expression.Constant(~Int32.MinValue, typeof(int))), 
            Expression.Constant(16, typeof(int)));

        //return (value.flags & ~Int32.MinValue) >> 16

        return Expression.Lambda<GetDigitsDelegate>(digits, value);
    }
}

This compiled code is assigned to the GetDigits field. Note that the function receives the decimal value as ref, so no actual copying is performed - only a reference to the value. Using the GetDigits function from the DecimalHelper is easy:

这个编译后的代码被分配给getnumbers字段。注意,该函数接受decimal值作为ref,因此不执行实际的复制——只对值进行引用。从DecimalHelper使用getnumbers函数很容易:

decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);

This is the fastest possible method for getting number of digits after decimal point for decimal values.

这是在十进制数之后获得数字数的最快的方法。

#5


10  

you can use the InvariantCulture

你可以使用不变性文化

string priceSameInAllCultures = price.ToString(System.Globalization.CultureInfo.InvariantCulture);

another possibility would be to do something like that:

另一种可能是做这样的事情:

private int GetDecimals(decimal d, int i = 0)
{
    decimal multiplied = (decimal)((double)d * Math.Pow(10, i));
    if (Math.Round(multiplied) == multiplied)
        return i;
    return GetDecimals(d, i+1);
}

#6


5  

And here's another way, use the type SqlDecimal which has a scale property with the count of the digits right of the decimal. Cast your decimal value to SqlDecimal and then access Scale.

这是另一种方法,使用类型SqlDecimal,它有一个scale属性和小数位数的计数。将十进制值转换为SqlDecimal,然后访问Scale。

((SqlDecimal)(decimal)yourValue).Scale

#7


4  

Relying on the internal representation of decimals is not cool.

依靠小数的内部表示是不酷的。

How about this:

这个怎么样:

    int CountDecimalDigits(decimal n)
    {
        return n.ToString(System.Globalization.CultureInfo.InvariantCulture)
                //.TrimEnd('0') uncomment if you don't want to count trailing zeroes
                .SkipWhile(c => c != '.')
                .Skip(1)
                .Count();
    }

#8


4  

Most people here seem to be unaware that decimal considers trailing zeroes as significant for storage and printing.

这里的大多数人似乎都不知道decimal可以将后跟0作为存储和打印的重要参数。

So 0.1m, 0.10m and 0.100m may compare as equal, they are stored differently (as value/scale 1/1, 10/2 and 100/3, respectively), and will be printed as 0.1, 0.10 and 0.100, respectively, by ToString().

所以0.1m、0.10m和0.100m可以进行相等的比较,它们的存储方式不同(分别为value/scale 1/1、10/2和100/3),ToString()将分别打印为0.1、0.10和0.100。

As such, the solutions that report "too high a precision" are actually reporting the correct precision, on decimal's terms.

因此,报告“精度太高”的解决方案实际上是按照十进制来报告正确的精度。

In addition, math-based solutions (like multiplying by powers of 10) will likely be very slow (decimal is ~40x slower than double for arithmetic, and you don't want to mix in floating-point either because that's likely to introduce imprecision). Similarly, casting to int or long as a means of truncating is error-prone (decimal has a much greater range than either of those - it's based around a 96-bit integer).

此外,基于数学的解决方案(比如乘以10的乘幂)可能会非常慢(对于算术来说,小数点要比双倍慢大约40倍,而且您也不希望混合使用浮点数,因为这可能会导致不精确)。类似地,将对象转换为int或long作为截断的方法是容易出错的(decimal的范围比这两种都大得多——它基于一个96位整数)。

While not elegant as such, the following will likely be one of the fastest way to get the precision (when defined as "decimal places excluding trailing zeroes"):

虽然不那么优雅,但以下可能是获得精度的最快方法之一(当定义为“不包括尾随零的小数点”时):

public static int PrecisionOf(decimal d) {
  var text = d.ToString(System.Globalization.CultureInfo.InvariantCulture).TrimEnd('0');
  var decpoint = text.IndexOf('.');
  if (decpoint < 0)
    return 0;
  return text.Length - decpoint - 1;
}

The invariant culture guarantees a '.' as decimal point, trailing zeroes are trimmed, and then it's just a matter of seeing of how many positions remain after the decimal point (if there even is one).

不变文化保证a。“作为小数点,后面的零被削减了,这只是一个问题,看看小数点后还有多少位(如果有的话)。”

Edit: changed return type to int

编辑:将返回类型更改为int

#9


1  

I wrote a concise little method yesterday that also returns the number of decimal places without having to rely on any string splits or cultures which is ideal:

我昨天写了一个简洁的小方法,它也返回小数位数,而不需要依赖任何字符串分割或文化,这是理想的:

public int GetDecimalPlaces(decimal decimalNumber) { // 
try {
    // PRESERVE:BEGIN
        int decimalPlaces = 1;
        decimal powers = 10.0m;
        if (decimalNumber > 0.0m) {
            while ((decimalNumber * powers) % 1 != 0.0m) {
                powers *= 10.0m;
                ++decimalPlaces;
            }
        }
return decimalPlaces;

#10


1  

You can try:

你可以尝试:

int priceDecimalPlaces =
        price.ToString(System.Globalization.CultureInfo.InvariantCulture)
              .Split('.')[1].Length;

#11


1  

I use the following mechanism in my code

我在代码中使用以下机制。

  public static int GetDecimalLength(string tempValue)
    {
        int decimalLength = 0;
        if (tempValue.Contains('.') || tempValue.Contains(','))
        {
            char[] separator = new char[] { '.', ',' };
            string[] tempstring = tempValue.Split(separator);

            decimalLength = tempstring[1].Length;
        }
        return decimalLength;
    }

decimal input=3.376; var instring=input.ToString();

小数输入= 3.376;var instring = input.ToString();

call GetDecimalLength(instring)

调用GetDecimalLength(instring)

#12


0  

I suggest using this method :

我建议使用这种方法:

    public static int GetNumberOfDecimalPlaces(decimal value, int maxNumber)
    {
        if (maxNumber == 0)
            return 0;

        if (maxNumber > 28)
            maxNumber = 28;

        bool isEqual = false;
        int placeCount = maxNumber;
        while (placeCount > 0)
        {
            decimal vl = Math.Round(value, placeCount - 1);
            decimal vh = Math.Round(value, placeCount);
            isEqual = (vl == vh);

            if (isEqual == false)
                break;

            placeCount--;
        }
        return Math.Min(placeCount, maxNumber); 
    }

#13


0  

Using recursion you can do:

使用递归可以做到:

private int GetDecimals(decimal n, int decimals = 0)  
{  
    return n % 1 != 0 ? GetDecimals(n * 10, decimals + 1) : decimals;  
}

#1


118  

I used Joe's way to solve this issue :)

我用乔的方式来解决这个问题:

decimal argument = 123.456m;
int count = BitConverter.GetBytes(decimal.GetBits(argument)[3])[2];

#2


16  

Since none of the answers supplied were good enough for the magic number "-0.01f" converted to decimal.. i.e: GetDecimal((decimal)-0.01f);
I can only assume a colossal mind-fart virus attacked everyone 3 years ago :)
Here is what seems to be a working implementation to this evil and monstrous problem, the very complicated problem of counting the decimal places after the point - no strings, no cultures, no need to count the bits and no need to read math forums.. just simple 3rd grade math.

由于所提供的答案中没有一个是足够的好到可以将“-0.01f”转换成十进制。我。艾凡:GetDecimal((十进制)-0.01 f);我只能承担一个巨大的mind-fart病毒攻击每个人3年前:)这是什么似乎是一个工作实现这邪恶和巨大的问题,很复杂的计算问题点后的小数点后——无附带条件的,没有文化,不需要计算部分和不需要阅读数学论坛. .只是简单的三年级数学。

public static class MathDecimals
{
    public static int GetDecimalPlaces(decimal n)
    {
        n = Math.Abs(n); //make sure it is positive.
        n -= (int)n;     //remove the integer part of the number.
        var decimalPlaces = 0;
        while (n > 0)
        {
            decimalPlaces++;
            n *= 10;
            n -= (int)n;
        }
        return decimalPlaces;
    }
}

private static void Main(string[] args)
{
    Console.WriteLine(1/3m); //this is 0.3333333333333333333333333333
    Console.WriteLine(1/3f); //this is 0.3333333

    Console.WriteLine(MathDecimals.GetDecimalPlaces(0.0m));                  //0
    Console.WriteLine(MathDecimals.GetDecimalPlaces(1/3m));                  //28
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)(1 / 3f)));     //7
    Console.WriteLine(MathDecimals.GetDecimalPlaces(-1.123m));               //3
    Console.WriteLine(MathDecimals.GetDecimalPlaces(43.12345m));             //5
    Console.WriteLine(MathDecimals.GetDecimalPlaces(0));                     //0
    Console.WriteLine(MathDecimals.GetDecimalPlaces(0.01m));                 //2
    Console.WriteLine(MathDecimals.GetDecimalPlaces(-0.001m));               //3
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)-0.00000001f)); //8
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)0.0001234f));   //7
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)0.01f));        //2
    Console.WriteLine(MathDecimals.GetDecimalPlaces((decimal)-0.01f));       //2
}

#3


15  

I'd probably use the solution in @fixagon's answer.

我可能会在@fixagon的答案中使用这个解决方案。

However, while the Decimal struct doesn't have a method to get the number of decimals, you could call Decimal.GetBits to extract the binary representation, then use the integer value and scale to compute the number of decimals.

然而,尽管Decimal struct没有方法来获取小数,但是可以调用Decimal。GetBits提取二进制表示,然后使用整数值和缩放来计算小数的数量。

This would probably be faster than formatting as a string, though you'd have to be processing an awful lot of decimals to notice the difference.

这可能比字符串格式要快,尽管您需要处理大量小数才能注意到差异。

I'll leave the implementation as an exercise.

我将把实现留作练习。

#4


13  

One of the best solutions for finding the number of digits after the decimal point is shown in burning_LEGION's post.

在burning_LEGION的文章中显示了找到小数点后数字的最好的解决方案之一。

Here I am using parts from a STSdb forum article: Number of digits after decimal point.

这里我使用的部分来自STSdb论坛文章:小数点后的位数。

In MSDN we can read the following explanation:

在MSDN中,我们可以看到以下解释:

"A decimal number is a floating-point value that consists of a sign, a numeric value where each digit in the value ranges from 0 to 9, and a scaling factor that indicates the position of a floating decimal point that separates the integral and fractional parts of the numeric value."

“十进制数是一个浮点值,它包含一个符号、一个数值,该数值中的每个数字都在0到9之间,以及一个缩放因子,该因子指示一个浮点数的位置,该浮点数分隔数值的整数部分和小数部分。”

And also:

还有:

"The binary representation of a Decimal value consists of a 1-bit sign, a 96-bit integer number, and a scaling factor used to divide the 96-bit integer and specify what portion of it is a decimal fraction. The scaling factor is implicitly the number 10, raised to an exponent ranging from 0 to 28."

十进制值的二进制表示由一个1位符号、一个96位整数和一个缩放因子组成,该因子用于除96位整数并指定它的哪个部分是十进制分数。比例因子是10的隐含值,它被提升到从0到28的指数。

On internal level the decimal value is represented by four integer values.

在内部级别上,十进制值由四个整数值表示。

找到小数位数,无论文化如何

There is a publicly available GetBits function for getting the internal representation. The function returns an int[] array:

有一个公共可用的GetBits函数来获取内部表示。函数返回一个int[]数组:

[__DynamicallyInvokable] 
public static int[] GetBits(decimal d)
{
    return new int[] { d.lo, d.mid, d.hi, d.flags };
}

The fourth element of the returned array contains a scale factor and a sign. And as the MSDN says the scaling factor is implicitly the number 10, raised to an exponent ranging from 0 to 28. This is exactly what we need.

返回数组的第四个元素包含比例因子和符号。正如MSDN所说的比例因子隐含地是数字10,指数从0到28。这正是我们所需要的。

Thus, based on all above investigations we can construct our method:

因此,基于以上研究,我们可以构建我们的方法:

private const int SIGN_MASK = ~Int32.MinValue;

public static int GetDigits4(decimal value)
{
    return (Decimal.GetBits(value)[3] & SIGN_MASK) >> 16;
}

Here a SIGN_MASK is used to ignore the sign. After logical and we have also shifted the result with 16 bits to the right to receive the actual scale factor. This value, finally, indicates the number of digits after the decimal point.

这里使用SIGN_MASK来忽略符号。在符合逻辑之后,我们还将16位的结果向右移动,以接收实际的比例因子。这个值最后表示小数点后的位数。

Note that here MSDN also says the scaling factor also preserves any trailing zeros in a Decimal number. Trailing zeros do not affect the value of a Decimal number in arithmetic or comparison operations. However, trailing zeros might be revealed by the ToString method if an appropriate format string is applied.

请注意,这里MSDN还说缩放因子也将所有尾随的0保留为小数。在算术或比较操作中,尾零不影响十进制数的值。但是,如果应用了适当的格式字符串,则ToString方法可能会显示尾随零。

This solutions looks like the best one, but wait, there is more. By accessing private methods in C# we can use expressions to build a direct access to the flags field and avoid constructing the int array:

这个解看起来是最好的,但是等等,还有更多。通过访问c#中的私有方法,我们可以使用表达式来构建对flags字段的直接访问,并避免构造int数组:

public delegate int GetDigitsDelegate(ref Decimal value);

public class DecimalHelper
{
    public static readonly DecimalHelper Instance = new DecimalHelper();

    public readonly GetDigitsDelegate GetDigits;
    public readonly Expression<GetDigitsDelegate> GetDigitsLambda;

    public DecimalHelper()
    {
        GetDigitsLambda = CreateGetDigitsMethod();
        GetDigits = GetDigitsLambda.Compile();
    }

    private Expression<GetDigitsDelegate> CreateGetDigitsMethod()
    {
        var value = Expression.Parameter(typeof(Decimal).MakeByRefType(), "value");

        var digits = Expression.RightShift(
            Expression.And(Expression.Field(value, "flags"), Expression.Constant(~Int32.MinValue, typeof(int))), 
            Expression.Constant(16, typeof(int)));

        //return (value.flags & ~Int32.MinValue) >> 16

        return Expression.Lambda<GetDigitsDelegate>(digits, value);
    }
}

This compiled code is assigned to the GetDigits field. Note that the function receives the decimal value as ref, so no actual copying is performed - only a reference to the value. Using the GetDigits function from the DecimalHelper is easy:

这个编译后的代码被分配给getnumbers字段。注意,该函数接受decimal值作为ref,因此不执行实际的复制——只对值进行引用。从DecimalHelper使用getnumbers函数很容易:

decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);

This is the fastest possible method for getting number of digits after decimal point for decimal values.

这是在十进制数之后获得数字数的最快的方法。

#5


10  

you can use the InvariantCulture

你可以使用不变性文化

string priceSameInAllCultures = price.ToString(System.Globalization.CultureInfo.InvariantCulture);

another possibility would be to do something like that:

另一种可能是做这样的事情:

private int GetDecimals(decimal d, int i = 0)
{
    decimal multiplied = (decimal)((double)d * Math.Pow(10, i));
    if (Math.Round(multiplied) == multiplied)
        return i;
    return GetDecimals(d, i+1);
}

#6


5  

And here's another way, use the type SqlDecimal which has a scale property with the count of the digits right of the decimal. Cast your decimal value to SqlDecimal and then access Scale.

这是另一种方法,使用类型SqlDecimal,它有一个scale属性和小数位数的计数。将十进制值转换为SqlDecimal,然后访问Scale。

((SqlDecimal)(decimal)yourValue).Scale

#7


4  

Relying on the internal representation of decimals is not cool.

依靠小数的内部表示是不酷的。

How about this:

这个怎么样:

    int CountDecimalDigits(decimal n)
    {
        return n.ToString(System.Globalization.CultureInfo.InvariantCulture)
                //.TrimEnd('0') uncomment if you don't want to count trailing zeroes
                .SkipWhile(c => c != '.')
                .Skip(1)
                .Count();
    }

#8


4  

Most people here seem to be unaware that decimal considers trailing zeroes as significant for storage and printing.

这里的大多数人似乎都不知道decimal可以将后跟0作为存储和打印的重要参数。

So 0.1m, 0.10m and 0.100m may compare as equal, they are stored differently (as value/scale 1/1, 10/2 and 100/3, respectively), and will be printed as 0.1, 0.10 and 0.100, respectively, by ToString().

所以0.1m、0.10m和0.100m可以进行相等的比较,它们的存储方式不同(分别为value/scale 1/1、10/2和100/3),ToString()将分别打印为0.1、0.10和0.100。

As such, the solutions that report "too high a precision" are actually reporting the correct precision, on decimal's terms.

因此,报告“精度太高”的解决方案实际上是按照十进制来报告正确的精度。

In addition, math-based solutions (like multiplying by powers of 10) will likely be very slow (decimal is ~40x slower than double for arithmetic, and you don't want to mix in floating-point either because that's likely to introduce imprecision). Similarly, casting to int or long as a means of truncating is error-prone (decimal has a much greater range than either of those - it's based around a 96-bit integer).

此外,基于数学的解决方案(比如乘以10的乘幂)可能会非常慢(对于算术来说,小数点要比双倍慢大约40倍,而且您也不希望混合使用浮点数,因为这可能会导致不精确)。类似地,将对象转换为int或long作为截断的方法是容易出错的(decimal的范围比这两种都大得多——它基于一个96位整数)。

While not elegant as such, the following will likely be one of the fastest way to get the precision (when defined as "decimal places excluding trailing zeroes"):

虽然不那么优雅,但以下可能是获得精度的最快方法之一(当定义为“不包括尾随零的小数点”时):

public static int PrecisionOf(decimal d) {
  var text = d.ToString(System.Globalization.CultureInfo.InvariantCulture).TrimEnd('0');
  var decpoint = text.IndexOf('.');
  if (decpoint < 0)
    return 0;
  return text.Length - decpoint - 1;
}

The invariant culture guarantees a '.' as decimal point, trailing zeroes are trimmed, and then it's just a matter of seeing of how many positions remain after the decimal point (if there even is one).

不变文化保证a。“作为小数点,后面的零被削减了,这只是一个问题,看看小数点后还有多少位(如果有的话)。”

Edit: changed return type to int

编辑:将返回类型更改为int

#9


1  

I wrote a concise little method yesterday that also returns the number of decimal places without having to rely on any string splits or cultures which is ideal:

我昨天写了一个简洁的小方法,它也返回小数位数,而不需要依赖任何字符串分割或文化,这是理想的:

public int GetDecimalPlaces(decimal decimalNumber) { // 
try {
    // PRESERVE:BEGIN
        int decimalPlaces = 1;
        decimal powers = 10.0m;
        if (decimalNumber > 0.0m) {
            while ((decimalNumber * powers) % 1 != 0.0m) {
                powers *= 10.0m;
                ++decimalPlaces;
            }
        }
return decimalPlaces;

#10


1  

You can try:

你可以尝试:

int priceDecimalPlaces =
        price.ToString(System.Globalization.CultureInfo.InvariantCulture)
              .Split('.')[1].Length;

#11


1  

I use the following mechanism in my code

我在代码中使用以下机制。

  public static int GetDecimalLength(string tempValue)
    {
        int decimalLength = 0;
        if (tempValue.Contains('.') || tempValue.Contains(','))
        {
            char[] separator = new char[] { '.', ',' };
            string[] tempstring = tempValue.Split(separator);

            decimalLength = tempstring[1].Length;
        }
        return decimalLength;
    }

decimal input=3.376; var instring=input.ToString();

小数输入= 3.376;var instring = input.ToString();

call GetDecimalLength(instring)

调用GetDecimalLength(instring)

#12


0  

I suggest using this method :

我建议使用这种方法:

    public static int GetNumberOfDecimalPlaces(decimal value, int maxNumber)
    {
        if (maxNumber == 0)
            return 0;

        if (maxNumber > 28)
            maxNumber = 28;

        bool isEqual = false;
        int placeCount = maxNumber;
        while (placeCount > 0)
        {
            decimal vl = Math.Round(value, placeCount - 1);
            decimal vh = Math.Round(value, placeCount);
            isEqual = (vl == vh);

            if (isEqual == false)
                break;

            placeCount--;
        }
        return Math.Min(placeCount, maxNumber); 
    }

#13


0  

Using recursion you can do:

使用递归可以做到:

private int GetDecimals(decimal n, int decimals = 0)  
{  
    return n % 1 != 0 ? GetDecimals(n * 10, decimals + 1) : decimals;  
}