Introduction

Recently, I ran into a practical problem: I had a list of users that I wanted to split into batches of 100. Pretty straightforward, but when I tried to calculate how many batches I needed, the result was a float. That wasn’t very helpful — I just wanted a number without any decimals. During this process, I encountered a few small challenges that I’d like to share with you in this blog post. I’ll walk you through my approach, the decisions I made, and how I eventually solved it.


[math]::round()

The easiest way to convert a float to an int is by using [math]::round(). Let’s take a look at an example.

$UserCount = 439
$MaxBatchSize = 100

$BatchesCount = $UserCount / $MaxBatchSize
Write-Output "Batches (float): $BatchesCount"

$BatchesCountRounded = [math]::round($BatchesCount)
Write-Output "Batches (rounded): $BatchesCountRounded"

Output

Batches (float): 4.39
Batches (rounded): 4

And here we already see the first challenge. If we divide 439 users into batches of 100, we don’t get 4 batches — we get 5. The last batch may not be a full 100 users, but it’s still a batch.

So you might think: “Then I’ll just add 1 every time!” That seems logical… until you look at the following example:

$UserCount = 561
$MaxBatchSize = 100

$BatchesCount = $UserCount / $MaxBatchSize
Write-Output "Batches (float): $BatchesCount"

$BatchesCountRounded = [math]::round($BatchesCount) + 1
Write-Output "Batches (rounded): $BatchesCountRounded"

Output

Batches (float): 5.61
Batches (rounded): 7

This happens because [math]::round() rounds up or down based on a threshold of x.5, and in the case of exactly x.5, it considers the nearest even number.


Rounding to x decimal places

We can also round to x decimal places using [math]::round(). This is done by adding an extra argument: [math]::round($float,$x).

And for the jokers who try to stack it — yes, that works. Just be aware that your first rounding will result in exactly x.5, so it will be rounded to the nearest even number.

## nearest even number: 2
[math]::round($([math]::round(1.49,1)))
2

## nearest even number: 2
[math]::round($([math]::round(2.49,1)))
2

## nearest even number: 4
[math]::round($([math]::round(3.49,1)))
4

Quick explanation of what we’re doing here: first we round, for example, 1.49 to 1 decimal place, and then we round the result to 0 decimal places.


Declare rounding strategy

We can avoid this behavior by explicitly specifying the rounding strategy. This is done using a third argument called MidpointRounding. This is an Enum with the following possible values:

NameValueDescription
ToEven0Rounds to the nearest number, and when halfway between two, it rounds toward the nearest even number.
AwayFromZero1Rounds to the nearest number, and when halfway between two, it rounds away from zero.
ToZero2Directed rounding toward zero, with the result closest to and no greater in magnitude than the infinitely precise result.
ToNegativeInfinity3Downward-directed rounding, with the result closest to and no greater than the infinitely precise result.
ToPositiveInfinity4Upward-directed rounding, with the result closest to and no less than the infinitely precise result.

If we revisit the previous example and use AwayFromZero (1) as the rounding strategy, we get the following:

[math]::round($([math]::round(1.49,1)),0,1)
2

[math]::round($([math]::round(2.49,1)),0,1)
3

[math]::round($([math]::round(3.49,1)),0,1)
4

Quick explanation: first we round, for example, 1.49 to 1 decimal place, and then we round the result to 0 decimal places, specifying that we want to use the AwayFromZero (1) strategy.


Other rounding methods

The Math class also offers two other methods for rounding numbers: Ceiling() and Floor().


[math]::Ceiling()

Ceiling() always rounds up.

[math]::Ceiling(5.61)
6

[math]::Ceiling(4.39)
5

So 1.001 with [math]::Ceiling() will always become 2.


[math]::Floor()

Floor() always rounds down.

[math]::Floor(5.61)
5

[math]::Floor(4.39)
4

So 1.9999 with [math]::Floor() will always become 1.


Conclusion

To solve my scenario, I ultimately used [math]::Ceiling(). This allowed me to reliably determine how many batches I needed.

If I had wanted to see only the number of full batches, I could have used [math]::Floor(). And if I wanted a more general rounding approach, I could have used [math]::Round(). Just keep in mind that this method can sometimes give slightly different results than you might expect.

Leave a Reply

Your email address will not be published. Required fields are marked *