37

What's the best way to round down to nearest whole number in PowerShell?

I am trying [math]::truncate but its not giving me predictable results.

Example:

$bla = 17.2/0.1
[math]::truncate($bla) 

outputs 171 instead of the expected 172!

$bla = 172
[math]::truncate($bla) 

outputs 172

I just need something that works.... and must always round down (i.e round($myNum + 0.5) won't work due to baker's rounding which may round up if the number has a 0.5 component).

4 Answers 4

42

Ah, I see. Looks like the datatype needs to be decimal:

[decimal] $de = 17.2/.1
[double] $db = 17.2/.1

[math]::floor($de)
172
[math]::floor($db)
171

http://msdn.microsoft.com/en-us/library/system.math.floor(v=vs.85).aspx

1
  • If you don't want to run into floating point errors, yes, the type should be decimal.
    – jpmc26
    Commented Apr 13, 2018 at 21:47
27

The Math::Floor function combined with [decimal] declaration should give you the results you want.

[Math]::Floor([decimal](17.27975/0.1))

returns = 172

2
  • Spoke too soon... [math]::floor(17.2/0.1) outputs 171, I need this to output 172.
    – ted
    Commented May 3, 2011 at 0:49
  • 2
    @Louis: Because floating-point numbers cannot represent 17.2 and 0.1 exactly which is why you're getting rounding errors in the last places and thus a result close to, but not quite 172.
    – Joey
    Commented May 3, 2011 at 5:18
9

The issue you are encountering with the original 17.2/0.1 division example is due to inaccuracy in the floating-point representation of the given decimal values (as mentioned in Joey's comment on another answer). You can see this in PowerShell by examining the round-trip representation of the final value:

PS> $bla = 17.2/0.1
PS> $bla.GetType().FullName
System.Double
PS> $bla.ToString()
172
PS> $bla.ToString('r')
171.99999999999997

A simple way to get around this is to declare the result as int, as PowerShell will automatically round to the the result to the nearest integer value:

PS> [int]$bli = 17.2/0.1
PS> $bli.GetType().FullName
System.Int32
PS> $bli.ToString()
172

Note that this uses the default .NET method of MidpointRounding.ToEven (also known as banker's rounding). This has nice statistical properties when tabulating large numbers of numeric values, but can also be changed to the simpler away-from-zero method:

function round( $value, [MidpointRounding]$mode = 'AwayFromZero' ) {
  [Math]::Round( $value, $mode )
}

PS> [int]3.5
4
PS> [int]4.5
4
PS> round 3.5
4
PS> round 4.5
5

Another option is to use a more accurate representation for the original values, which will avoid the issue entirely:

PS> $bld = [decimal]17.2/0.1
PS> $bld.GetType().FullName
System.Decimal
PS> $bld.ToString()
172
1
  • +1 for this Post_ID being the same as my UserID.
    – ashleedawg
    Commented Jul 30, 2018 at 3:48
6

[Math]::floor($x) is the built-in way to do it.

Just be aware of how it will behave with negative numbers. [Math]::floor(5.5) returns 5, but [Math]::floor(-5.5) returns -6.

If you need the function to return the value closest to zero, you'll need:

If ($x -ge 0) {
    [Math]::Floor($x)
} Else {
    [Math]::Ceiling($x)
}
3
  • 8
    To return the value closest to zero you can use [Math]::Truncate.
    – Joey
    Commented May 3, 2011 at 5:21
  • Your code is mathemaically seen incorrect. When you want to round down, then you want exactly the result Floor gives you also for the neative values! Commented Aug 31, 2022 at 9:54
  • @MehrdadMirreza No, "round down" is not a well-defined term when it comes to negative values, and the original question doesn't state which behavior is required. Sometimes people will mean "towards zero" when they say down, and sometimes they mean "to the lower value" when they say down. Reading the other answers to this question should make it very clear that it's quite ambiguous, let alone the range of functions available to accomplish it in the CLR.
    – Bacon Bits
    Commented Aug 31, 2022 at 14:32

Not the answer you're looking for? Browse other questions tagged or ask your own question.