在我这边误解之后,在阅读问题的答案How to get numbers to a specific decimal place时...
引用问题摘要:
问: Feii Momo想知道:如何将金额舍入到 0.05 步内最接近的值。
A:Enigmativity提供的解决方案是将值乘以 20 并四舍五入并至少除以 20
Math.Round(value * 20.0m, 0) / 20.0m
...我想出了一个更通用的问题:
Are there any practical advantages/disadvantages between these two approaches:
(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
为:
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 次迭代,所有三个都执行安静相等(约 70 毫秒)。而且执行起来似乎没有什么区别。
但只是为了检查是否有任何意外惊喜,我 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# 中所预期的那样与四舍五入到所需的小数位数相比,四舍五入和除法没有任何性能优势。
所以重复我的问题:
Are there any practical advantages/disadvantages between these two approaches:
(I) var rValue = Math.Round(value * 10.0m , 0) / 10.0m; (II) var rValue = Math.Round(value, 1);
最佳答案
对更新问题的简短回答
您实际上可以在CoreCLR中看到当前FCallRound
实现的代码。 。如果您经过ecalllist.h#L784 ,您可能会看到它映射到 COMDecimal::DoRound
这又将大部分逻辑委托(delegate)给 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
> 是适合 32 位的 10 的最大幂)。这意味着性能差异可能有两个潜在来源:
- 您四舍五入到超过 9 位有效数字,因此如果您先相乘,循环将会运行更多次
-
Div96By32
如果相乘后尾数有更多非零 32 位字,则工作速度可能会变慢。
所以,是的,当带有乘法的代码运行速度较慢时,您可以创建一个人工示例。例如,如果您以
开头,则可以利用差异 #2decimal value = 92233720368.547758m
尾数为 ≈ 2^63/100
。那么即使您不考虑计算时间, Math.Round(value, 4)
也会比 Math.Round(value*10000, 0)
更快值*10000
(see online)。
不过,我认为在任何现实生活中的使用中,您都不会注意到任何显着差异。
原始长答案
我认为您错过了所引用问题的全部要点。主要问题是飞沫沫想要将40.23四舍五入为40.25。这个精度不等于小数点后的某个整数!使用四舍五入到指定的小数位,您将得到 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
和除法都花费相同的时间,与它们的第二个参数无关。请注意,在第二个示例中,您没有避免第一个示例的任何基本步骤!所以事实并非如此
Why should it be easier to round and divide instead of round to a specified fractional digit?
一个人是否比另一个人更好几乎纯粹是主观的事情。 (我可以想到一些边缘情况,第二个不会失败,而第一个会失败,但只要我们谈论原始问题中提到的任何实际金额,它们就无关紧要。)
总结一下:
- 您可以避免乘法和除法并仅使用 Math.Round 吗?很可能不会
- 你能乘以和除以一个不能被 10 整除的数字吗?会有什么不同吗?是的你可以。不,几乎可以肯定不会有什么区别。
关于c# - System.Math.Round - 舍入到零位并除以舍入到一位或多位数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48216487/