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
所以我可以看到,从文字创建十进制变量可以让我控制精度。
.NET 1.1 中引入了这样的保留尾随零,以更严格地符合 ECMA CLI 规范。
MSDN 上有一些关于此的信息,例如这里。
您可以按如下方式调整精度:
Math.Round(或 Ceiling、Floor 等)降低精度(b 来自 c)
乘以 1.000...(您想要的小数位数)以提高精度 - 例如a 乘以 1.0M 得到 b。
您只是看到完全相同数据的不同表示形式。
decimal
的精度将缩放至所需的大小(在合理范围内)。
System.Decimal
:
十进制数是浮点数 由符号、a 组成的值 数值,其中每个数字 值范围从 0 到 9,并且 a 比例因子表示 浮动小数点的位置 将积分和 数值的小数部分。
十进制的二进制表示 值由 1 位符号、 96 位整数和缩放 用于划分 96 位的因子 整数并指定它的哪一部分 是小数。缩放比例 因子隐式是数字 10, 提升至 0 范围内的指数 到 28。因此,二进制 Decimal 值的表示形式是 形式为 ((-296 至 296) / 10(0 至 28)),其中 -296-1 等于 MinValue,且 296-1 等于 最大值。
缩放因子还保留任何 十进制数中的尾随零。 尾随零不影响 十进制数的值 算术或比较运算。 然而,尾随零可以是 由 ToString 方法显示,如果 应用适当的格式字符串。
那又怎么样 Math.Round(小数 d, int 小数)?
我发现我可以通过乘以或除以一个奇特的 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;
很容易将 SQL Server 中的
decimal
与 .NET 中的 decimal
混淆;他们完全不同。
SQL Server
decimal
是一个定点数,当定义列或变量时,其精度和小数位数是固定的。
A .NET
decimal
是浮点数,类似于 float
和 double
(区别在于 decimal
准确保留十进制数字,而 float
和 double
准确保留二进制数字)。尝试控制 .NET decimal
的精度是没有意义的,因为无论是否存在填充零,所有计算都会产生相同的结果。
这将从小数点中删除所有尾随零,然后您可以只使用
ToString()
。
public static class DecimalExtensions
{
public static Decimal Normalize(this Decimal value)
{
return value / 1.000000000000000000000000000000000m;
}
}
或者,如果您想要精确数量的尾随零,例如 5,请先使用 Normalize(),然后乘以 1.00000m。
问题是 - 您是否真的需要以小数形式存储的精度,而不仅仅是将小数显示为所需的精度。 大多数应用程序内部都知道它们想要达到的精确度,并以该精确度进行显示。例如,即使用户在帐户包中输入 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
public static decimal SetScale(this decimal input, int scale)
{
if (scale < 0)
throw new ArgumentOutOfRangeException(nameof(scale));
if (input.Scale == scale)
return input;
//normalize
input /= 1.000000000000000000000000000000000m;
var scaleToadd = scale - input.Scale;
for(int i = 0; i < scaleToadd; i++)
{
input *= 1.0M;
}
return input;
}
并在 getter 上使用它来强制尾随零,例如作为
:
private decimal price;
public decimal Price { get => price.SetScale(2); set => price = value; }