.net 为什么 Math.Round(2.5) 返回 2 而不是 3?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/977796/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 12:50:40  来源:igfitidea点击:

Why does Math.Round(2.5) return 2 instead of 3?

.netrounding

提问by jeffu

In C#, the result of Math.Round(2.5)is 2.

在 C# 中,结果Math.Round(2.5)为 2。

It is supposed to be 3, isn't it? Why is it 2 instead in C#?

应该是3吧?为什么在 C# 中用 2 代替?

回答by Jon Skeet

Firstly, this wouldn't be a C# bug anyway - it would be a .NET bug. C# is the language - it doesn't decide how Math.Roundis implemented.

首先,这无论如何都不是 C# 错误——而是 .NET 错误。C# 是语言 - 它不决定如何Math.Round实现。

And secondly, no - if you read the docs, you'll see that the default rounding is "round to even" (banker's rounding):

其次,不 - 如果您阅读docs,您会看到默认舍入是“舍入到偶数”(银行家的舍入):

Return Value
Type: System.Double
The integer nearest a. If the fractional component of a is halfway between two integers, one of which is even and the other odd, then the even number is returned. Note that this method returns a Doubleinstead of an integral type.

Remarks
The behavior of this method follows IEEE Standard 754, section 4. This kind of rounding is sometimes called rounding to nearest, or banker's rounding. It minimizes rounding errors that result from consistently rounding a midpoint value in a single direction.

返回值
类型:System.Double
最接近 a 的整数。如果 a 的小数部分介于两个整数之间,其中一个是偶数,另一个是奇数,则返回偶数。请注意,此方法返回一个Double而不是整数类型。

备注
此方法的行为遵循 IEEE 标准 754,第 4 节。这种舍入有时称为舍入到最接近,或银行家舍入。它最大限度地减少了由于在单个方向上对中点值进行一致舍入而导致的舍入误差。

You can specify how Math.Roundshould round mid-points using an overloadwhich takes a MidpointRoundingvalue. There's one overload with a MidpointRoundingcorresponding to each of the overloads which doesn't have one:

您可以Math.Round使用带值的重载指定如何舍入中点MidpointRounding。有一个重载,MidpointRounding对应于每个没有一个的重载:

Whether this default was well chosen or not is a different matter. (MidpointRoundingwas only introduced in .NET 2.0. Before then I'm not sure there was any easy way of implementing the desired behaviour without doing it yourself.) In particular, history has shown that it's not the expectedbehaviour - and in most cases that's a cardinal sin in API design. I can see whyBanker's Rounding is useful... but it's still a surprise to many.

是否选择了这个默认值是另一回事。(MidpointRounding仅在 .NET 2.0 中引入。在那之前,我不确定是否有任何简单的方法来实现所需的行为而无需自己动手。)特别是,历史表明这不是预期的行为 - 在大多数情况下是API 设计中的大罪。我可以理解为什么银行家的舍入很有用……但对许多人来说仍然是一个惊喜。

You may be interested to take a look at the nearest Java equivalent enum (RoundingMode) which offers even more options. (It doesn't just deal with midpoints.)

您可能有兴趣查看最近的 Java 等效枚举 ( RoundingMode),它提供了更多选项。(它不只是处理中点。)

回答by paxdiablo

That's called rounding to even (or banker's rounding), which is a valid rounding strategy for minimizing accrued errors in sums (MidpointRounding.ToEven). The theory is that, if you always round a 0.5 number in the same direction, the errors will accrue faster (round-to-even is supposed to minimize that) (a).

这称为四舍五入到偶数(或银行家的四舍五入),这是一种有效的舍入策略,用于最小化 sums 中的应计误差(MidpointRounding.ToEven)。理论是,如果你总是在同一个方向上取整 0.5 个数字,误差会更快地累积(四舍五入应该最小化)(a)

Follow these links for the MSDN descriptions of:

请按照以下链接获取 MSDN 描述:

  • Math.Floor, which rounds down towards negative infinity.
  • Math.Ceiling, which rounds up towards positive infinity.
  • Math.Truncate, which rounds up or down towards zero.
  • Math.Round, which rounds to the nearest integer or specified number of decimal places. You can specify the behavior if it's exactly equidistant between two possibilities, such as rounding so that the final digit is even ("Round(2.5,MidpointRounding.ToEven)" becoming 2) or so that it's further away from zero ("Round(2.5,MidpointRounding.AwayFromZero)" becoming 3).
  • Math.Floor,向下舍入到负无穷大。
  • Math.Ceiling,向上取整为正无穷大。
  • Math.Truncate,向上或向下舍入到零。
  • Math.Round,四舍五入到最接近的整数或指定的小数位数。如果它在两种可能性之间完全等距,您可以指定行为,例如四舍五入以使最后一位数字为偶数(“ Round(2.5,MidpointRounding.ToEven)” 变为 2)或使其远离零(“ Round(2.5,MidpointRounding.AwayFromZero)”变为 3)。

The following diagram and table may help:

下面的图表和表格可能会有所帮助:

-3        -2        -1         0         1         2         3
 +--|------+---------+----|----+--|------+----|----+-------|-+
    a                     b       c           d            e

                       a=-2.7  b=-0.5  c=0.3  d=1.5  e=2.8
                       ======  ======  =====  =====  =====
Floor                    -3      -1      0      1      2
Ceiling                  -2       0      1      2      3
Truncate                 -2       0      0      1      2
Round(ToEven)            -3       0      0      2      3
Round(AwayFromZero)      -3      -1      0      2      3

Note that Roundis a lot more powerful than it seems, simply because it can round to a specific number of decimal places. All the others round to zero decimals always. For example:

请注意,这Round比看起来要强大得多,仅仅是因为它可以四舍五入到特定的小数位数。所有其他人总是四舍五入到零小数。例如:

n = 3.145;
a = System.Math.Round (n, 2, MidpointRounding.ToEven);       // 3.14
b = System.Math.Round (n, 2, MidpointRounding.AwayFromZero); // 3.15

With the other functions, you have to use multiply/divide trickery to achieve the same effect:

对于其他函数,您必须使用乘法/除法技巧才能达到相同的效果:

c = System.Math.Truncate (n * 100) / 100;                    // 3.14
d = System.Math.Ceiling (n * 100) / 100;                     // 3.15


(a)Of course, that theory depends on the fact that your data has an fairly even spread of values across the even halves (0.5, 2.5, 4.5, ...) and odd halves (1.5, 3.5, ...).

(a)当然,该理论取决于这样一个事实,即您的数据在偶数一半(0.5、2.5、4.5...)和奇数一半(1.5、3.5...)之间具有相当均匀的值分布。

If allthe "half-values" are evens (for example), the errors will accumulate just as fast as if you always rounded up.

如果所有的“半值”都是偶数(例如),则错误累积的速度与您总是向上取整一样快。

回答by Michael Petrotta

From MSDN, Math.Round(double a)returns:

MSDN,Math.Round(double a)返回:

The integer nearest a. If the fractional component of a is halfway between two integers, one of which is even and the other odd, then the even number is returned.

最接近 a 的整数。如果 a 的小数部分介于两个整数之间,其中一个是偶数,另一个是奇数,则返回偶数。

... and so 2.5, being halfway between 2 and 3, is rounded down to the even number (2). this is called Banker's Rounding(or round-to-even), and is a commonly-used rounding standard.

... 所以 2.5,介于 2 和 3 之间,四舍五入为偶数 (2)。这称为银行家舍入(或舍入到偶数),是常用的舍入标准。

Same MSDN article:

同样的 MSDN 文章:

The behavior of this method follows IEEE Standard 754, section 4. This kind of rounding is sometimes called rounding to nearest, or banker's rounding. It minimizes rounding errors that result from consistently rounding a midpoint value in a single direction.

此方法的行为遵循 IEEE 标准 754 第 4 节。这种舍入有时称为最接近的舍入或银行家舍入。它最大限度地减少了由于在单个方向上对中点值进行一致舍入而导致的舍入误差。

You can specify a different rounding behavior by calling the overloads of Math.Round that take a MidpointRoundingmode.

您可以通过调用采用MidpointRounding模式的 Math.Round 的重载来指定不同的舍入行为。

回答by Dirk Vollmar

You should check MSDN for Math.Round:

您应该检查 MSDN 以了解Math.Round

The behavior of this method follows IEEE Standard 754, section 4. This kind of rounding is sometimes called rounding to nearest, or banker's rounding.

此方法的行为遵循 IEEE 标准 754 第 4 节。这种舍入有时称为最接近的舍入或银行家舍入。

You can specify the behavior of Math.Roundusing an overload:

您可以指定Math.Round使用重载的行为:

Math.Round(2.5, 0, MidpointRounding.AwayFromZero); // gives 3

Math.Round(2.5, 0, MidpointRounding.ToEven); // gives 2

回答by Patrick Peters

The nature of rounding

四舍五入的性质

Consider the task of rounding a number that contains a fraction to, say, a whole number. The process of rounding in this circumstance is to determine which whole number best represents the number you are rounding.

考虑将包含分数的数字四舍五入为整数的任务。在这种情况下,四舍五入的过程是确定哪个整数最能代表您要四舍五入的数字。

In common, or 'arithmetic' rounding, it is clear that 2.1, 2.2, 2.3 and 2.4 round to 2.0; and 2.6, 2.7, 2.8 and 2.9 to 3.0.

通常,或“算术”舍入,很明显 2.1、2.2、2.3 和 2.4 舍入为 2.0;和 2.6、2.7、2.8 和 2.9 到 3.0。

That leaves 2.5, which is no nearer to 2.0 than it is to 3.0. It is up to you to choose between 2.0 and 3.0, either would be equally valid.

剩下 2.5,它与 2.0 并不比与 3.0 更接近。您可以在 2.0 和 3.0 之间进行选择,两者都同样有效。

For minus numbers, -2.1, -2.2, -2.3 and -2.4, would become -2.0; and -2.6, 2.7, 2.8 and 2.9 would become -3.0 under arithmetic rounding.

对于负数,-2.1、-2.2、-2.3 和 -2.4 将变为 -2.0;在算术四舍五入下,-2.6、2.7、2.8 和 2.9 将变为 -3.0。

For -2.5 a choice is needed between -2.0 and -3.0.

对于 -2.5,需要在 -2.0 和 -3.0 之间进行选择。

Other forms of rounding

其他形式的舍入

'Rounding up' takes any number with decimal places and makes it the next 'whole' number. Thus not only do 2.5 and 2.6 round to 3.0, but so do 2.1 and 2.2.

“四舍五入”取任何带小数位的数字,并使其成为下一个“整数”数字。因此,不仅 2.5 和 2.6 舍入到 3.0,而且 2.1 和 2.2 也是如此。

Rounding up moves both positive and negative numbers away from zero. Eg. 2.5 to 3.0 and -2.5 to -3.0.

向上舍入使正数和负数远离零。例如。2.5 到 3.0 和 -2.5 到 -3.0。

'Rounding down' truncates numbers by chopping off unwanted digits. This has the effect of moving numbers towards zero. Eg. 2.5 to 2.0 and -2.5 to -2.0

“四舍五入”通过去掉不需要的数字来截断数字。这具有将数字移向零的效果。例如。2.5 到 2.0 和 -2.5 到 -2.0

In "banker's rounding" - in its most common form - the .5 to be rounded is rounded either up or down so that the result of the rounding is always an even number. Thus 2.5 rounds to 2.0, 3.5 to 4.0, 4.5 to 4.0, 5.5 to 6.0, and so on.

在“银行家的四舍五入”中 - 以最常见的形式 - 要四舍五入的 .5 向上或向下四舍五入,因此四舍五入的结果始终是偶数。因此,2.5 轮到 2.0,3.5 到 4.0,4.5 到 4.0,5.5 到 6.0,等等。

'Alternate rounding' alternates the process for any .5 between rounding down and rounding up.

“交替舍入”在向下舍入和向上舍入之间交替处理任何 0.5。

'Random rounding' rounds a .5 up or down on an entirely random basis.

“随机舍入”在完全随机的基础上向上或向下舍入 0.5。

Symmetry and asymmetry

对称与不对称

A rounding function is said to be 'symmetric' if it either rounds all numbers away from zero or rounds all numbers towards zero.

如果舍入函数从零舍入所有数字或将所有数字向零舍入,则称舍入函数是“对称的”。

A function is 'asymmetric' if rounds positive numbers towards zero and negative numbers away from zero.. Eg. 2.5 to 2.0; and -2.5 to -3.0.

如果将正数向零舍入而将负数向远离零舍入,则函数是“不对称的”。例如。2.5 到 2.0;和 -2.5 到 -3.0。

Also asymmetric is a function that rounds positive numbers away from zero and negative numbers towards zero. Eg. 2.5 to 3.0; and -2.5 to -2.0.

非对称也是一种将正数从零舍入并将负数向零舍入的函数。例如。2.5 到 3.0;和 -2.5 到 -2.0。

Most of time people think of symmetric rounding, where -2.5 will be rounded towards -3.0 and 3.5 will be rounded towards 4.0.(in C# Round(AwayFromZero))

大多数时候人们会想到对称舍入,其中 -2.5 将向 -3.0 舍入,3.5 将向 4.0 舍入。(在 C# 中Round(AwayFromZero)

回答by Chris S

The default MidpointRounding.ToEven, or Bankers' rounding (2.5 become 2, 4.5 becomes 4 and so on) has stung me before with writing reports for accounting, so I'll write a few words of what I found out, previously and from looking into it for this post.

默认值MidpointRounding.ToEven或银行家四舍五入(2.5 变为 2,4.5 变为 4 等等)之前在编写会计报告时让我感到震惊,所以我将写一些我之前发现的以及从调查中发现的内容这个帖子。

Who are these bankers that are rounding down on even numbers (British bankers perhaps!)?

这些对偶数进行四舍五入的银行家是谁(也许是英国银行家!)?

From wikipedia

来自维基百科

The origin of the term bankers' rounding remains more obscure. If this rounding method was ever a standard in banking, the evidence has proved extremely difficult to find. To the contrary, section 2 of the European Commission report The Introduction of the Euro and the Rounding of Currency Amounts suggests that there had previously been no standard approach to rounding in banking; and it specifies that "half-way" amounts should be rounded up.

银行家四舍五入一词的起源仍然更加模糊。如果这种四舍五入方法曾经是银行业的标准,那么证据已经证明是极难找到的。相反,欧盟委员会报告“欧元的引入和货币金额的四舍五入”的第 2 节表明,以前银行业没有四舍五入的标准方法;并指定“半途”金额应四舍五入。

It seems a very strange way of rounding particularly for banking, unless of course banks use to receive lots of deposits of even amounts. Deposit £2.4m, but we'll call it £2m sir.

这似乎是一种非常奇怪的四舍五入方式,尤其是对于银行业而言,当然除非银行用来接收大量偶数存款。押金 £2.4m,但我们称它为 £2m 先生。

The IEEE Standard 754 dates back to 1985 and gives both ways of rounding, but with banker's as the recommended by the standard. This wikipedia articlehas a long list of how languages implement rounding (correct me if any of the below are wrong) and most don't use Bankers' but the rounding you're taught at school:

IEEE 标准 754 可追溯到 1985 年,并提供了两种四舍五入的方法,但标准建议使用银行家。这篇维基百科文章列出了一长串语言如何实现舍入(如果以下任何一条错误,请纠正我)并且大多数不使用银行家,而是您在学校教授的舍入:

  • C/C++round() from math.h rounds away from zero (not banker's rounding)
  • JavaMath.Roundrounds away from zero (it floors the result, adds 0.5, casts to an integer). There's an alternative in BigDecimal
  • Perluses a similar way to C
  • Javascript is the same as Java's Math.Round.
  • 来自 math.h 的C/C++round() 从零开始舍入(不是银行家的舍入)
  • Java Math.Round 从零开始舍入(将结果取整,加上 0.5,转换为整数)。BigDecimal 中有一个替代方案
  • Perl使用类似于 C 的方式
  • Javascript 与 Java 的 Math.Round 相同。

回答by Cristian Donoso

From MSDN:

来自 MSDN:

By default, Math.Round uses MidpointRounding.ToEven. Most people are not familiar with "rounding to even" as the alternative, "rounding away from zero" is more commonly taught in school. .NET defaults to "Rounding to even" as it is statistically superior because it doesn't share the tendency of "rounding away from zero" to round up slightly more often than it rounds down (assuming the numbers being rounded tend to be positive.)

默认情况下,Math.Round 使用 MidpointRounding.ToEven。大多数人不熟悉“四舍五入”作为替代方案,“从零舍入”在学校中更常见。.NET 默认为“四舍五入到偶数”,因为它在统计上更胜一筹,因为它不具有“从零舍入”的趋势,向上舍入的频率比向下舍入的频率略高(假设被舍入的数字往往是正数。 )

http://msdn.microsoft.com/en-us/library/system.math.round.aspx

http://msdn.microsoft.com/en-us/library/system.math.round.aspx

回答by JBrooks

Since Silverlight doesn't support the MidpointRounding option you have to write your own. Something like:

由于 Silverlight 不支持 MidpointRounding 选项,因此您必须自己编写。就像是:

public double RoundCorrect(double d, int decimals)
{
    double multiplier = Math.Pow(10, decimals);

    if (d < 0)
        multiplier *= -1;

    return Math.Floor((d * multiplier) + 0.5) / multiplier;

}

For the examples including how to use this as an extension see the post: .NET and Silverlight Rounding

有关包括如何将其用作扩展的示例,请参阅帖子:.NET and Silverlight Rounding

回答by ShortFuse

I had this problem where my SQL server rounds up 0.5 to 1 while my C# application didn't. So you would see two different results.

我遇到了这个问题,我的 SQL 服务器将 0.5 舍入到 1,而我的 C# 应用程序却没有。所以你会看到两种不同的结果。

Here's an implementation with int/long. This is how Java rounds.

这是一个 int/long 的实现。这就是 Java 的循环方式。

int roundedNumber = (int)Math.Floor(d + 0.5);

It's probably the most efficient method you could think of as well.

这可能也是您能想到的最有效的方法。

If you want to keep it a double and use decimal precision , then it's really just a matter of using exponents of 10 based on how many decimal places.

如果您想将其保留为 double 并使用小数精度,那么实际上只是根据小数位数使用 10 的指数的问题。

public double getRounding(double number, int decimalPoints)
{
    double decimalPowerOfTen = Math.Pow(10, decimalPoints);
    return Math.Floor(number * decimalPowerOfTen + 0.5)/ decimalPowerOfTen;
}

You can input a negative decimal for decimal points and it's word fine as well.

您可以为小数点输入负小数,也可以。

getRounding(239, -2) = 200

回答by Nalan Madheswaran

Simple way is:

简单的方法是:

Math.Ceiling(decimal.Parse(yourNumber + ""));