Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/svg/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 查找十进制值中的小数位数,而不考虑区域性_C#_Decimal_Cultureinfo - Fatal编程技术网

C# 查找十进制值中的小数位数,而不考虑区域性

C# 查找十进制值中的小数位数,而不考虑区域性,c#,decimal,cultureinfo,C#,Decimal,Cultureinfo,我想知道是否有一种简洁而准确的方法来提取十进制值(作为int)中的小数位数,这样可以安全地跨不同的区域性信息使用 例如: 19.0应返回1, 27.5999应返回4, 19.12应返回2, 等等 我编写了一个查询,在句点上拆分字符串以查找小数点: int priceDecimalPlaces = price.ToString().Split('.').Count() > 1 ? price.ToString().Split('.').ToList().

我想知道是否有一种简洁而准确的方法来提取十进制值(作为int)中的小数位数,这样可以安全地跨不同的区域性信息使用

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

我编写了一个查询,在句点上拆分字符串以查找小数点:

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

但我觉得这只适用于使用“.”作为十进制分隔符的区域,因此在不同的系统中非常脆弱。

您可以使用不变量区域性

string priceSameInAllCultures = price.ToString(System.Globalization.CultureInfo.InvariantCulture);
另一种可能是这样做:

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);
}
您可以尝试:

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

我可能会用这个解决方案

但是,虽然Decimal结构没有获取小数位数的方法,但是可以调用提取二进制表示,然后使用整数值和小数位数计算小数位数

这可能比格式化为字符串要快,不过您必须处理大量的小数才能注意到差异

我将把实现留作练习。

我曾经解决过这个问题:)


我昨天写了一个简洁的小方法,它还返回小数位数,而不必依赖任何字符串拆分或区域性,这是理想的:

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;

我在代码中使用以下机制

  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;
    }
十进制输入=3.376; var instring=input.ToString()


调用GetDecimalLength(instring)

查找小数点后位数的最佳解决方案之一,如所示

在这里,我使用的部分来自STDB论坛文章:

在MSDN中,我们可以阅读以下说明:

“十进制数是一种浮点值,它由一个符号和一个数字组成,其中每个数字的范围从0到9, 以及一个比例因子,用于指示分隔数值的整数部分和小数部分的浮点小数点的位置。”

而且:

十进制值的二进制表示法由1位符号、96位整数和用于除以96位整数的比例因子组成 并指定它的哪一部分是小数。比例因子隐式地是数字10,提升为0到28的指数。”

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

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

[__DynamicallyInvokable] 
public static int[] GetBits(decimal d)
{
    return new int[] { d.lo, d.mid, d.hi, d.flags };
}
返回数组的第四个元素包含比例因子和符号。正如MSDN所说,比例因子隐含着数字10,它的指数范围从0到28。这正是我们需要的

因此,基于上述所有调查,我们可以构建我们的方法:

private const int SIGN_MASK = ~Int32.MinValue;

public static int GetDigits4(decimal value)
{
    return (Decimal.GetBits(value)[3] & SIGN_MASK) >> 16;
}
这里使用符号屏蔽来忽略符号。在逻辑和之后,我们还将结果右移16位,以接收实际的比例因子。最后,该值表示小数点后的位数

请注意,这里MSDN还表示缩放因子还保留十进制数中的任何尾随零。尾随零不会影响算术或比较运算中十进制数的值。但是,如果应用了适当的格式字符串,ToString方法可能会显示尾随的零

这个解决方案看起来是最好的,但是等等,还有更多。通过,我们可以使用表达式构建对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);
    }
}

这是获取十进制值小数点后位数的最快方法。

因为提供的答案都不足以将神奇数字“-0.01f”转换为十进制。。i、 e:
GetDecimal((十进制)-0.01f)
我只能假设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;
    }
}


这里的大多数人似乎没有意识到,十进制认为尾随的零对于存储和打印非常重要

因此,0.1m、0.10m和0.100m可能比较相等,它们的存储方式不同(分别为值/刻度1/1、10/2和100/3),并将通过
ToString()
分别打印为0.1、0.10和0.100

因此,报告“精度太高”的解决方案实际上报告了正确的精度,以
十进制
的术语来说

此外,基于数学的解决方案(如乘以10的幂)可能会非常慢(对于算术,十进制比双精度慢约40倍,并且您也不希望混合使用浮点,因为这可能会引入不精确性)。类似地,将强制转换为
int
long
作为截断的一种方法也容易出错(
decimal
的范围比这两种方法的范围大得多-它基于96位整数)

虽然这样做并不优雅,但以下可能是获得精度的最快方法之一(定义为“小数位数,不包括尾随的零”):

公共静态整数精度(十进制d){
var text=d.ToString(System.Globalization.CultureInfo.InvariantCulture.TrimEnd('0');
var decpoint=text.IndexOf('.');
if(decpoint<0)
返回0;
返回text.Length-decpoint-1;
}
不变区域性保证将“.”作为小数点,尾部的零将被修剪,然后只需查看小数点后保留了多少个位置(如果有)


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

依赖小数的内部表示并不酷

这个怎么样:
decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);
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
}
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;
}
    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();
    }
    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); 
    }
((SqlDecimal)(decimal)yourValue).Scale
private int GetDecimals(decimal n, int decimals = 0)  
{  
    return n % 1 != 0 ? GetDecimals(n * 10, decimals + 1) : decimals;  
}
    // NOTE: Do not change the order in which these fields are declared. The
    // native methods in this class rely on this particular order.
    private int flags;
    private int hi;
    private int lo;
    private int mid;
internal static class DecimalExtensions
{
  public static byte GetScale(this decimal value)
  {
    unsafe
    {
      byte* v = (byte*)&value;
      return v[2];
    }
  }
}
[StructLayout(LayoutKind.Explicit)]
public struct DecimalHelper
{
    const byte k_SignBit = 1 << 7;

    [FieldOffset(0)]
    public decimal Value;

    [FieldOffset(0)]
    public readonly uint Flags;
    [FieldOffset(0)]
    public readonly ushort Reserved;
    [FieldOffset(2)]
    byte m_Scale;
    public byte Scale
    {
        get
        {
            return m_Scale;
        }
        set
        {
            if(value > 28)
                throw new System.ArgumentOutOfRangeException("value", "Scale can't be bigger than 28!")
            m_Scale = value;
        }
    }
    [FieldOffset(3)]
    byte m_SignByte;
    public int Sign
    {
        get
        {
            return m_SignByte > 0 ? -1 : 1;
        }
    }
    public bool Positive
    {
        get
        {
            return (m_SignByte & k_SignBit) > 0 ;
        }
        set
        {
            m_SignByte = value ? (byte)0 : k_SignBit;
        }
    }
    [FieldOffset(4)]
    public uint Hi;
    [FieldOffset(8)]
    public uint Lo;
    [FieldOffset(12)]
    public uint Mid;

    public DecimalHelper(decimal value) : this()
    {
        Value = value;
    }

    public static implicit operator DecimalHelper(decimal value)
    {
        return new DecimalHelper(value);
    }

    public static implicit operator decimal(DecimalHelper value)
    {
        return value.Value;
    }
}
string number = "123.456789"; // Convert to string
int length = number.Substring(number.IndexOf(".") + 1).Length;  // 6
private int GetSignificantDecimalPlaces(decimal number, bool trimTrailingZeros = true)
{
  string stemp = Convert.ToString(number);

  if (trimTrailingZeros)
    stemp = stemp.TrimEnd('0');

  return stemp.Length - 1 - stemp.IndexOf(
         Application.CurrentCulture.NumberFormat.NumberDecimalSeparator);
}