Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have a interesting question on ints with decimals.

Assuming I do the following:

[int] $a = 5/2
$a

I've tried it 10 times to be sure, and powershell always returns 2

Is there a way to force Powershell to round up or down in such circumstances and by default has it been set to round down?

I'm assuming depending on the machine and Powershell environment, I may get 3 at some points and 2 at others.

like image 604
Aiden Avatar asked Feb 19 '18 10:02

Aiden


Video Answer


2 Answers

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

Floor will give you the preceding whole number and Ceiling will be providing the succeeding whole number. But if you want to round it up, using the Round function, it will follow midpoint rounding (Rounding at midpoint is historically away from zero), as demonstrated below -

[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

You can use either functions depending upon your requirement.

like image 196
Vivek Kumar Singh Avatar answered Oct 17 '22 06:10

Vivek Kumar Singh


Vivek Kumar's answer is helpful, but has some confusing aspects.

Generally, converting a fractional number to an integer invariably involves a form of rounding; in the context of casting and implicit conversion, programming languages typically use a form of rounding to the nearest integer. Special considerations apply to the ambiguous case of numbers whose fractional part is exactly .5, for which more than one strategy exists - and different programming languages employ different strategies.

In the context of the .NET Framework, on which PowerShell is built, the umbrella term for these strategies is midpoint rounding, and the specific strategy names used below refer to the midpoint (.5) as half for brevity (the examples below use PowerShell syntax, but apply to all .NET languages).

  • Casting to [int] invariably employs half-to-even rounding, where numbers with a fractional part of .5 are rounded to the nearest even integer (whether positive or negative):

    • [int] 2.5 -> 2 (!) situational down-rounding, because the integer part happens to be even and positive
    • [int] 3.5 -> 4 situational up-rounding
    • This rounding strategy also applies to implicit conversions to integer types that PowerShell sometimes performs - see last section.
    • PowerShell syntax pitfall: a cast has higher precedence than /, so [int] 5/2 does not work as intended; use [int] (5/2).
  • To control the midpoint rounding behavior, use the .NET [Math] class' Round() method:

    • Use [int] [Math]::Round($number, [MidpointRounding]::AwayFromZero) to get half-away-from-zero rounding (numbers with a fraction of .5 are rounded to the nearest integer whose absolute value is larger).

      • [Math]::Round(2.5, [MidpointRounding]::AwayFromZero) -> 3
      • [Math]::Round(-2.5, [MidpointRounding]::AwayFromZero) -> -3
      • Note: The [Math] methods (typically) return a [double], so you may have to cast the result to [int] (or a different integer type) to get a true integer.

Note that [Math]::Round() offers not only to-integer rounding, but also to a specific number of fractional digits; e.g.,
[Math]::Round(2.55, 1, [MidpointRounding]::AwayFromZero) yields 2.6.Thanks, Ansgar Wiechers.


Other forms of rounding: Those where the specific value of the fractional part (other than 0) is irrelevant:

  • Use [Math]::Truncate($number) to get toward-zero rounding (removal of the fractional part):

    • [Math]::Truncate(2.1) -> 2; ditto for 2.5 and 2.9, for instance
    • [Math]::Truncate(-2.1) -> -2
  • Use [Math]::Ceiling($number) to get toward-positive-infinity rounding (rounding up to the nearest greater-or-equal integer):

    • [Math]::Ceiling(2.1) -> 3
    • [Math]::Ceiling(-2.1) -> -2 (!)
  • Use [int] [Math]::Floor($number) to get toward-negative-infinity rounding (rounding down to the nearest smaller-or-equal integer):

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

Optional further reading:

An example of PowerShell performing an implicit conversion in which this strategy is used:

1..(2.5) yields array 1, 2, because the endpoint of the range-operator expression, 2.5, was coerced to [int] 2, so the expression is effectively the same as 1..2

Since PowerShell is built on top of the .NET Framework, it is ultimately [Convert]::ToInt32() that is called.

The intent behind the perhaps surprising round-half-to-even strategy is "to minimize the expected error when summing over rounded figures", according to Wikipedia.

Wikipedia's page on rounding has a section on rounding functions across programming languages.

In contrast with .NET, JavaScript, for instance, employs half-up rounding (Math.round(2.5) -> 3, Math.round(-2.5) -> -2) - a midpoint-rounding mode that .NET doesn't even offer.

like image 24
mklement0 Avatar answered Oct 17 '22 05:10

mklement0