5

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.

mklement0
  • 245,023
  • 45
  • 419
  • 492
Aiden
  • 151
  • 2
  • 12
  • What you're seeing is often referred to as `integer math`. You're essentially forcing a casting of the result to an integer; which just drops anything after the decimal point. The result will be the same in any PS environment. – gvee Feb 19 '18 at 11:03
  • 2
    @gvee what you describe would be the `[Math]::Truncate` or the `[Math]::Floor` function. Powershell, however, uses the `[Math]::Round` function when casting a decimal to an int. – J. Bergmann Feb 19 '18 at 11:52
  • 2
    I used `[Math]::Floor` as an interchangeable for `[Math]Truncate` in my previous comment. That would only be true if you work with positive numbers. For negative numbers, `[Math]::Ceiling` would work the same way `[Math]::Truncate` does. – J. Bergmann Feb 19 '18 at 12:04
  • @J.Bergmann you are right! I did not know that. – gvee Feb 19 '18 at 12:04

2 Answers2

8
[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.

Vivek Kumar Singh
  • 2,776
  • 1
  • 13
  • 21
  • 3
    Rounding 2.5 to 2 isn't basic mathematics. This happens because Powershells default MidpointRounding mode is ToEven, so it will round ever .5 number to the nearest even integer. To use basic mathematics u can specify the MidpointRounding mode to AwayFromZero. – J. Bergmann Feb 19 '18 at 11:00
  • Is there a [Math]::Round() for 64 bit numbers in PowerShell 5.1 ? – Henk Poley Nov 20 '20 at 14:04
  • @HenkPoley - Yes I believe there is. Refer to [this](https://devblogs.microsoft.com/scripting/understanding-numbers-in-powershell/) blog. PowerShell supports 64-bit signed integer as long and an unsigned 64-bit integer as ulong. – Vivek Kumar Singh Nov 21 '20 at 05:19
7

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.

mklement0
  • 245,023
  • 45
  • 419
  • 492