调整十进制精度.net


67

这些在C#中的行

decimal a = 2m;
decimal b = 2.0m;
decimal c = 2.00000000m;
decimal d = 2.000000000000000000000000000m;

Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
Console.WriteLine(d);

生成此输出:

2
2.0
2.00000000
2.000000000000000000000000000

因此,我可以看到从文字中创建一个十进制变量可以控制精度。

  • 是否可以在不使用文字的情况下调整小数变量的精度?
  • 如何从a创建b?如何从c创建b?

Answers:


53

.NET 1.1中引入了这样的保留结尾零,以更严格地符合ECMA CLI规范。

有关MSDN的一些信息,例如here

您可以如下调整精度:

  • Math.Round(或Ceiling,Floor等)以降低精度(c中的b)

  • 乘以1.000 ...(与所需的小数位数)以提高精度-例如乘以1.0M从a中得到b。


9
另外划分1.000...,以减少精度。
Jeppe Stig Nielsen

18

您只是看到完全相同的数据的不同表示形式。的精度decimal将缩放为与所需一样大(在合理范围内)。

来自System.Decimal

十进制数是一个浮点值,它由一个符号,一个数值(其中值中的每个数字都介于0到9之间)以及一个比例因子(表示将整数和小数部分分开的浮点小数的位置)组成。数值的

十进制值的二进制表示形式包括一个1位符号,一个96位整数和一个比例因子,该比例因子用于对96位整数进行除法并指定其哪一部分为小数部分。缩放因子隐式为数字10,升至0到28之间的指数。因此,小数的二进制表示形式为((-2 96到2 96)/ 10 (0到28)) ,其中-2 96 -1等于MinValue,而2 96 -1等于MaxValue。

比例因子还保留十进制数中的任何尾随零。尾随零不会影响算术或比较操作中小数的值。但是,如果应用了适当的格式字符串,则ToString方法可以显示结尾的零。


2
有关比例系数的好信息...现在如何调整?
艾米B

1
缩放因子不可直接控制,因为它会根据指数的大小进行调整。这基本上就是浮点的工作方式(以及术语“ floating”来自何处)。由于有效位数的位数是恒定的,因此位数的大小决定了有效位数的大小。
codekaizen

它会自动调整以适应其中包含的数据的需求。您是否有特定原因需要手动缩放?
Andrew Hare

有一个原因-但是,我认为该原因并未使这个问题更加清晰。我可能会问一个单独的原因。
艾米B

@AndrewHare我只需要快速确认-我相信.net和SQL世界中十进制数据类型的精度(数字中的总位数)和小数位数(小数点后的总位数)的定义是相同的。我认为这些精度和比例概念与数学世界有关,而不是.Net或SQL。如果我错了,请纠正我。
RBT


6

decimalSQL Server与decimal.NET混淆是很诱人的。他们是完全不同的。

SQL Serverdecimal是定点数,在定义列或变量时其精度和小数位数是固定的。

甲.NETdecimal就像一个浮点数floatdouble(不同之处在于decimal准确蜜饯十进制数字,而floatdouble准确地保存二进制数)。试图控制.NET的精度decimal是没有意义的,因为无论填充零与否,所有计算都会产生相同的结果。


有趣的是,在EF中将小平面设置为小数会创建一个适当的SQL decimal属性,当将c# decimal其交给a时会立即失败...在我的情况下,我使用了Precision:2,Scale:(none),并且800由于值超出范围
ekkis 2011年

5

我发现我可以通过乘以或除以1来“篡改”音阶。

decimal a = 2m;
decimal c = 2.00000000m;
decimal PreciseOne = 1.000000000000000000000000000000m;
  //add maximum trailing zeros to a
decimal x = a * PreciseOne;
  //remove all trailing zeros from c
decimal y = c / PreciseOne;

我可以制造出足够精确的1来按已知尺寸更改比例因子。

decimal scaleFactorBase = 1.0m;
decimal scaleFactor = 1m;
int scaleFactorSize = 3;

for (int i = 0; i < scaleFactorSize; i++)
{
  scaleFactor *= scaleFactorBase;
}

decimal z = a * scaleFactor;

4
有趣,但同时令人困惑。2.0m和2.00000000m之间到底有什么区别?在非计算环境中,我将其表示为保证后者精确到该精度,而保证前者仅保证到小数点后第一位。但是,乘法应该意味着结果只能精确到小数点后一位。
sgmoore

1

问题是-您真的需要存储在十进制中的精度,而不是仅仅将十进制显示为所需的精度。大多数应用程序内部都知道它们要达到的精度并以该精度显示。例如,即使用户在帐户包中输入100的发票,它仍然会使用类似val.ToString(“ n2”)之类的内容打印为100.00。

如何从a创建b?如何从c创建b?

c到b是可能的。

Console.WriteLine(Math.Round(2.00000000m, 1)) 

产生2.0

a到b有点棘手,因为引入精度的概念与数学有点不同。

我想骇人听闻的骇客可能是一次往返。

decimal b = Decimal.Parse(a.ToString("#.0"));
Console.WriteLine(b);

产生2.0


1

这将从十进制中删除所有尾随零,然后可以使用ToString()

public static class DecimalExtensions
{
    public static Decimal Normalize(this Decimal value)
    {
        return value / 1.000000000000000000000000000000000m;
    }
}

或者,如果您想要精确的尾随零个数字(例如5),请首先进行Normalize(),然后乘以1.00000m。


不需要那么多的零,可以在之后加上28个零1。查看对其他答案的评论。
Jeppe Stig Nielsen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.