在我的一方误解之后,在阅读问题的答案时How to get numbers to a specific decimal place ...
参考问题的总结:
问: Feii Momo想知道:如何在0.05步内将金额四舍五入到最接近的值。
答: Enigmativity提供的解决方案是将值乘以20并将其四舍五入,至少将其除以20
Math.Round(value * 20.0m, 0) / 20.0m
......我想出了一个更通用的问题:
这两种方法之间是否有任何实际的优点/缺点:
(I) var rValue = Math.Round(value * 10.0m , 0) / 10.0m; (II) var rValue = Math.Round(value, 1);
到目前为止我做了什么:
起初我看了Docs - System.Math.Round,但我找不到任何提示。我还看一下Reference Source - Decimal Round以查看是否有任何不同的执行分支,但到目前为止它只出现:
public static Decimal Round(Decimal d, int decimals)
{
FCallRound (ref d, decimals);
return d;
}
和FCallRound
as:
private static extern void FCallRound(ref Decimal d, int decimals);
很遗憾,我没有为FCallRound
找到一些代码。
之后我想更实际地考虑它,并想看看是否有任何性能差异,在四舍五入到0位或1..n数字和"raced the horses"之间。
首先我run这三个函数调用:
(1) var rValue = Math.Round(value, 0);
(2) var rValue = Math.Round(value, 1);
(3) var rValue = Math.Round(value, 12);
这告诉我,对于1'000'000次迭代,所有三次执行安静相等(~70ms)。似乎执行没有区别。
但是为了检查任何意外的惊喜,我compared这些行:
(1) var rValue = Math.Round(value, 1);
(2) var rValue = Math.Round(value * 10.0m, 0);
(3) var rValue = Math.Round(value * 10.0m, 0) / 10.0m;
正如所料,每次乘法都会增加时间(每次约70毫秒)。
正如c#中预期的那样,舍入和除法没有性能优势,而是舍入到所需的小数位数。
重复我的问题:
这两种方法之间是否有任何实际的优点/缺点:
(I) var rValue = Math.Round(value * 10.0m , 0) / 10.0m; (II) var rValue = Math.Round(value, 1);
答案 0 :(得分:2)
简短(呃)回答更新后的问题
您实际上可以在CoreCLR中看到当前FCallRound
实施的代码。如果您通过ecalllist.h#L784,您可能会看到它已映射到COMDecimal::DoRound
,后者又将大部分逻辑委托给VarDecRound
。您可以在链接中看到完整的代码,但关键部分是:
iScale = pdecIn->u.u.scale - cDecimals;
do {
ulSticky |= ulRem;
if (iScale > POWER10_MAX)
ulPwr = ulTenToNine;
else
ulPwr = rgulPower10[iScale];
ulRem = Div96By32(rgulNum, ulPwr);
iScale -= 9;
} while (iScale > 0);
其中常量定义为
#define POWER10_MAX 9
static const ULONG ulTenToNine = 1000000000U;
static ULONG rgulPower10[POWER10_MAX+1] = {1, 10, 100, 1000, 10000, 100000, 1000000,
10000000, 100000000, 1000000000};
那么这段代码的作用是找出当前值应该移位多少小数位,然后分批进行分割,最多为10^9
(而10^9
是10的最大幂。 32位)。这意味着可能存在两个潜在的性能差异来源:
Div96By32
可能会更慢。所以是的,当带乘法的代码运行得更慢时,你可以创建一个人为的例子。例如,如果以
开头,则可以利用差异#2decimal value = 92233720368.547758m
尾数为≈2^63/100
的。即使您没有考虑到计算Math.Round(value, 4)
(see online)的时间,Math.Round(value*10000, 0)
也会比value*10000
更快。
我仍然认为,在任何现实生活中,你都不会注意到任何显着差异。
原来的长答案
我认为你错过了引用问题的全部内容。主要问题是Feii Momo希望40.2 3 四舍五入到40.2 5 。这是一个精度,不等于某些整数的十进制数字!使用舍入到指定的小数位数,您将获得40.23(> = 2位数)或40.2(0)(1位数)。乘法和除法的技巧是一个简单的技巧,可以在子位数精度上进行四舍五入(但只有当你的"子数位"是2的负幂时才有效,例如0.5或0.25 / 0.5 / 0.75等等)。此外,我不知道有任何其他简单的方法不使用这种乘法 - 舍入技巧。
是的,无论如何,当你进行乘法 - 舍入时,你是否做
并不重要var rValue = Math.Round(value * 20.0m, 0) / 20.0m;
或
var rValue = Math.Round(value * 2.0m, 1) / 2.0m;
因为Round
和除法都与第二个参数无关。请注意,在第二个示例中,您不必避免第一个示例中的任何必要步骤!所以它不是真的
为什么圆形和分割而不是舍入到指定的小数位更容易?
一个人是否优于其他人几乎是纯粹主观的事情。 (我可以想到一些边缘情况,当第二个不会失败时,第一个将会,但只要我们谈论原始问题中提到的任何现实的金额,它们就没有关系。)
总结一下: