Jes*_*ter 72 c# cultureinfo decimal
我想知道是否有一种简洁而准确的方法来提取十进制值中的小数位数(作为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;
Run Code Online (Sandbox Code Playgroud)
但我发现这只适用于使用'.'的地区.作为小数分隔符,因此在不同系统中非常脆弱.
bur*_*ION 144
我用Joe的方式来解决这个问题:)
decimal argument = 123.456m;
int count = BitConverter.GetBytes(decimal.GetBits(argument)[3])[2];
Run Code Online (Sandbox Code Playgroud)
G.Y*_*G.Y 20
由于所提供的答案都不足以将幻数"-0.01f"转换为十进制...即:GetDecimal((decimal)-0.01f);
我只能假设一个巨大的心灵屁病毒3年前袭击了所有人:)
这里似乎是一个工作实现这个邪恶和怪异的问题,在点之后计算小数位的非常复杂的问题 - 没有字符串,没有文化,不需要计算位数,也不需要阅读数学论坛..只是简单的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;
}
}
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)
Joe*_*Joe 18
我可能会在@ fixagon的答案中使用这个解决方案.
但是,虽然Decimal结构没有获取小数位数的方法,但可以调用Decimal.GetBits来提取二进制表示,然后使用整数值和scale来计算小数位数.
这可能比作为字符串格式化更快,尽管你必须处理大量的小数来注意区别.
我将把实施作为练习.
Kri*_*rov 15
找到小数点后的位数的最佳解决方案之一显示在burning_LEGION的帖子中.
这里我使用的是STSdb论坛文章中的部分:小数点后的位数.
在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 };
}
Run Code Online (Sandbox Code Playgroud)
返回数组的第四个元素包含比例因子和符号.并且正如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;
}
Run Code Online (Sandbox Code Playgroud)
这里SIGN_MASK用于忽略符号.在逻辑之后,我们还将结果向右移16位以接收实际比例因子.最后,该值表示小数点后的位数.
请注意,此处MSDN还说缩放因子也会保留十进制数中的任何尾随零.尾随零不会影响算术或比较运算中的十进制数的值.但是,如果应用了适当的格式字符串,ToString方法可能会显示尾随零.
这个解决方案看起来是最好的,但等等,还有更多.通过访问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);
}
}
Run Code Online (Sandbox Code Playgroud)
此编译的代码分配给GetDigits字段.请注意,该函数接收十进制值作为ref,因此不执行实际复制 - 仅对值的引用.使用DecimalHelper中的GetDigits函数很简单:
decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);
Run Code Online (Sandbox Code Playgroud)
这是获取小数点小数点后的位数的最快方法.
fix*_*gon 11
你可以使用InvariantCulture
string priceSameInAllCultures = price.ToString(System.Globalization.CultureInfo.InvariantCulture);
Run Code Online (Sandbox Code Playgroud)
另一种可能性是这样做:
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);
}
Run Code Online (Sandbox Code Playgroud)
Cle*_*ent 10
依赖于小数的内部表示并不酷.
这个怎么样:
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();
}
Run Code Online (Sandbox Code Playgroud)
这里的大多数人似乎都不知道小数会将尾随零视为存储和打印的重要因素.
所以0.1m,0.10m和0.100m可以比较相同,它们的存储方式不同(分别为值/比例1/1,10/2和100/3),分别打印为0.1,0.10和0.100 ,通过ToString()
.
因此,根据条款,报告"精度太高"的解决方案实际上报告了正确的精度decimal
.
此外,基于数学的解决方案(如乘以10的幂)可能会非常慢(十进制比算术的两倍慢约40倍,并且您不希望混合使用浮点,因为这可能会导致不精确).类似地,转换int
或long
作为截断的手段是容易出错的(decimal
具有比其中任何一个更大的范围 - 它基于96位整数).
虽然不如此优雅,但以下可能是获得精度的最快方法之一(定义为"排除尾随零的小数位"):
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;
}
Run Code Online (Sandbox Code Playgroud)
不变的文化保证了'.' 作为小数点,尾随零被修剪,然后它只是看到小数点后剩余多少个位置(如果有一个).
编辑:将返回类型更改为int
这是另一种方法,使用类型SqlDecimal,它具有scale属性,其中包含小数点右边的数字.将您的十进制值转换为SqlDecimal,然后访问Scale.
((SqlDecimal)(decimal)yourValue).Scale
Run Code Online (Sandbox Code Playgroud)
作为十进制扩展方法,考虑到:
public static class DecimalExtensions
{
public static int GetNumberDecimalPlaces(this decimal source)
{
var parts = source.ToString(CultureInfo.InvariantCulture).Split('.');
if (parts.Length < 2)
return 0;
return parts[1].TrimEnd('0').Length;
}
}
Run Code Online (Sandbox Code Playgroud)