Powershell:将分数转换为整数 - 令人惊讶的舍入行为

Powershell: convert a fraction to an integer - surprising rounding behavior

我有一个关于带小数的整数的有趣问题。

假设我执行以下操作:

[int] $a = 5/2
$a

我已经试了 10 次了,powershell 总是 returns 2

有没有办法强制 Powershell 在这种情况下向上或向下舍入,默认情况下是否设置为向下舍入?

我假设取决于机器和 Powershell 环境,我可能在某些时候得到 3 个,在其他时候得到 2 个。

[Math]::Floor($a) --> 2
[Math]::Ceiling($a)--> 3
[Math]::Round($a) --> 2

Floor 会给出前面的整数,Ceiling 会给出后面的整数。 但是如果你想向上舍入,使用 Round 函数,它将遵循中点舍入( 中点舍入在历史上远离零 ),如下所示 -

[Math]::Round(2.50) --> 2
[Math]::Round(2.51) --> 3
[Math]::Round(2.49) --> 2
[math]::Round(2.50,[System.MidpointRounding]::AwayFromZero) --> 3
[math]::Round(2.49,[System.MidpointRounding]::AwayFromZero) --> 2
[math]::Round(2.51,[System.MidpointRounding]::AwayFromZero) --> 3

您可以根据需要使用任一功能。

很有帮助,但有一些令人困惑的方面。

一般来说,将小数转换为整数总是涉及到四舍五入的形式;在 castingimplicit 转换的上下文中,编程语言通常使用 rounding to the nearest integer[=195= 的形式]. 特殊注意事项适用于小数部分正好是.5的模糊情况,其中多于一个存在策略 - 不同的编程语言采用不同的策略。

在构建 PowerShell 的 .NET Framework 的上下文中,这些策略的 总称 中点四舍五入,下面使用的具体策略名称指中点(.5)为half 为简洁起见(以下示例使用 PowerShell 语法,但适用于 所有 .NET 语言)。

  • 转换为[int] 总是采用half-to-even四舍五入,其中小数部分为.5的数字四舍五入到最接近的 even 整数(无论正数还是负数):

    • [int] 2.5 -> 2 (!) 情境向下舍入,因为整数部分恰好是偶数 和阳性
    • [int] 3.5 -> 4 情境 向上-四舍五入
    • 此舍入策略也适用于 隐式 到 PowerShell 有时执行的整数类型的转换 - 请参阅上一节。
    • PowerShell 语法陷阱:强制转换的优先级高于 /,因此 [int] 5/2 无效 如预期;使用 [int] (5/2).
  • 控制中点舍入行为,请使用.NET [Math] class' Round() 方法:

    • [int] [Math]::Round($number, [MidpointRounding]::AwayFromZero)得到half-away-from-zero rounding(分数为 .5 的数字四舍五入到最接近的整数,其 absolute 值是 larger)。

      • [Math]::Round(2.5, [MidpointRounding]::AwayFromZero) -> 3
      • [Math]::Round(-2.5, [MidpointRounding]::AwayFromZero) -> -3
      • 注意:[Math] 方法(通常)return a [double],因此您可能必须将结果转换为 [int](或不同的整数类型)得到一个真正的整数。

请注意,[Math]::Round() 不仅提供到-整数 舍入,而且还提供特定数量的小数位数;例如,
[Math]::Round(2.55, 1, [MidpointRounding]::AwayFromZero) 产生 2.6. 谢谢,Ansgar Wiechers.


其他形式的四舍五入:那些与小数部分(0除外)的具体值无关的:

  • 使用[Math]::Truncate($number)得到toward-zero四舍五入(去掉小数部分):

    • [Math]::Truncate(2.1) -> 22.52.9 同上,例如
    • [Math]::Truncate(-2.1) -> -2
  • 使用[Math]::Ceiling($number)得到toward-positive-infinity四舍五入(四舍五入到最接近的 greater-or-equal 整数):

    • [Math]::Ceiling(2.1) -> 3
    • [Math]::Ceiling(-2.1) -> -2 (!)
  • 使用[int] [Math]::Floor($number)得到toward-negative-infinity四舍五入(向下舍入到最接近的 smaller-or-equal 整数):

    • [Math]::Floor(2.1) -> 2
    • [Math]::Floor(-2.1) -> -3 (!)

可选的进一步阅读:

PowerShell 执行 隐式 转换的示例,其中使用了此策略:

1..(2.5) 产生数组 1, 2,因为 range-operator 表达式的端点 2.5 被强制为 [int] 2,所以表达式实际上与 1..2

相同

由于 PowerShell 建立在 .NET Framework 之上,因此最终 [Convert]::ToInt32() 被调用。

也许令人惊讶的 round-half-to-even 策略背后的意图是 "to minimize the expected error when summing over rounded figures", according to Wikipedia.

维基百科关于舍入的页面有一个关于 rounding functions across programming languages 的部分。

与 .NET 相比,JavaScript,例如,采用 half-up 舍入(Math.round(2.5) -> 3, Math.round(-2.5) -> -2) - .NET 甚至不提供的 midpoint-rounding 模式。