4 min read
Bulk Testing PowerShell Scripts with the Tokenizer
In Part 1, we explored the internal PowerShell parser to see how it analyzes code and breaks it down into individual...
Unlocking the Power of PowerShell: Tips for Success
In part 1, we began exploring how cmdlets internally rely on .NET libraries and how you can access these libraries directly. As we discovered, most libraries offer much more functionality than what is exposed by cmdlets.
In this part, we’ll explore the differences between types and libraries, and examine "overloads"—what they are and why they provide you with greater control. As always, we’ll use a practical example that is required in many scripts: mathematical operations and rounding.
Let’s first clarify the terminology. In part 1, we discussed .NET libraries. However, when you examine code—such as [System.IO.Path]—these libraries appear to be types. So, what are they? Libraries or types?
The answer is: both. In essence, they are classes. Each class can function as a type (for data storage), or as a library (for providing additional commands).
[int] is a well-known type and represents numbers:
PS C:\> [int]$a = 12.564
PS C:\> $a
13
As you can see, the number is stored, and the type ensures that the data is stored in a specific form. In this case, the floating-point number 12.564 was rounded by [int] to a whole number.
[int] can also be viewed as a library and supports the same tricks you saw in part 1, so you can access its properties and methods using a double colon:
PS C:\> [int]::MaxValue
2147483647
PS C:\> [int]::MinValue
-2147483648
Its properties, MaxValue and MinValue, conveniently help you look up the value range supported by this type, but the type doesn’t provide much else. It is designed for data storage.
Other types cannot store data. Instead, they provide commands (called methods) for a given task. [Math], for example, provides all the sophisticated mathematical methods you may require. The [Math] type acts more like a library of commands. You can use the trick from part 1 to list all the mathematical methods found in this library:
PS C:\> [Math] | Get-Member -Static
TypeName: System.Math
Name MemberType Definition
---- ---------- ----------
Abs Method static sbyte Abs(sbyte value), static int16 Abs(int16 value), static int Abs(int value), static long Abs(long value), static decimal Abs(decimal val...
Acos Method static double Acos(double d)
Asin Method static double Asin(double d)
Atan Method static double Atan(double d)
Atan2 Method static double Atan2(double y, double x)
BigMul Method static long BigMul(int a, int b)
Ceiling Method static decimal Ceiling(decimal d), static double Ceiling(double a)
Cos Method static double Cos(double d)
Cosh Method static double Cosh(double value)
DivRem Method static int DivRem(int a, int b, [ref] int result), static long DivRem(long a, long b, [ref] long result)
Equals Method static bool Equals(System.Object objA, System.Object objB)
Exp Method static double Exp(double d)
Floor Method static decimal Floor(decimal d), static double Floor(double d)
IEEERemainder Method static double IEEERemainder(double x, double y)
Log Method static double Log(double a, double newBase), static double Log(double d)
Log10 Method static double Log10(double d)
Max Method static sbyte Max(sbyte val1, sbyte val2), static byte Max(byte val1, byte val2), static int16 Max(int16 val1, int16 val2), static uint16 Max(uint16 ...
Min Method static sbyte Min(sbyte val1, sbyte val2), static byte Min(byte val1, byte val2), static int16 Min(int16 val1, int16 val2), static uint16 Min(uint16 ...
Pow Method static double Pow(double x, double y)
ReferenceEquals Method static bool ReferenceEquals(System.Object objA, System.Object objB)
Round Method static double Round(double value, int digits), static double Round(double value, System.MidpointRounding mode), static double Round(double value, in...
Sign Method static int Sign(sbyte value), static int Sign(int16 value), static int Sign(int value), static int Sign(long value), static int Sign(float value), s...
Sin Method static double Sin(double a)
Sinh Method static double Sinh(double value)
Sqrt Method static double Sqrt(double d)
Tan Method static double Tan(double a)
Tanh Method static double Tanh(double value)
Truncate Method static decimal Truncate(decimal d), static double Truncate(double d)
E Property static double E {get;}
PI Property static double PI {get;}
As always, with this knowledge, you can execute much more granular control. Let’s explore how [Math] can help you round values more effectively:
PS C:\> [int]12.564
13
PS C:\> [Math]::Round(12.564)
13
PS C:\> [Math]::Round(12.564,2)
12,56
Converting a float to an int always rounds to a whole number. After all, that’s what [int] requires, and when you round numbers this way, you are simply (ab)using the nature of [int].
Behind the scenes, [int] is calling a command to round numbers. When you do the same and directly call the rounding method, you initially get the same result (a whole number). However, now you have access to all overloads (alternative rounding methods). By submitting a second argument, you can control the number of digits you want to receive. In the example, the final call rounded to two digits.
As in part 1, you can view the overload definitions (alternative method calls) by omitting the parentheses:
PS C:\> [Math]::Round
OverloadDefinitions
-------------------
static double Round(double value, int digits)
static double Round(double value, System.MidpointRounding mode)
static double Round(double value, int digits, System.MidpointRounding mode)
static decimal Round(decimal d)
static decimal Round(decimal d, int decimals)
static decimal Round(decimal d, System.MidpointRounding mode)
static decimal Round(decimal d, int decimals, System.MidpointRounding mode)
static double Round(double a)
With Round(), you’ll see eight different ways to call the method. Keep in mind that overload definitions are intended for developers, not IT pros, so they often differ only in types. The functionally distinct calls are as follows:
OverloadDefinitions
-------------------
static double Round(double value)
static double Round(double value, int digits)
static double Round(double value, System.MidpointRounding mode)
static double Round(double value, int digits, System.MidpointRounding mode)
Here’s how the overload definitions can be deciphered by mere mortals—let’s pick one of the overloads:
static double Round(double value, int digits, System.MidpointRounding mode)
Overloads exist so you don’t have to use all the possible arguments if you don’t need them. Here, we are using three different overloads, and each additional overload accepts more arguments for more control:
$number = 12.545
[Math]::Round($number)
# -> 13
[Math]::Round($number, 2)
# -> 12.54
[Math]::Round($number, 2, [System.MidpointRounding]::ToEven)
# -> 12.54
[Math]::Round($number, 2, [System.MidpointRounding]::AwayFromZero)
# -> 12.55
Let’s assume you want to report the free space on your hard drive(s). Here are a few ways to do it.
Without any knowledge of rounding, this could be your solution:
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' |
ForEach-Object {
[PSCustomObject]@{
Drive = $_.DeviceID
'Size (MB)' = $_.Size/1MB
'Free Space (MB)' = $_.FreeSpace/1MB
PercentFree = $_.FreeSpace * 100 / $_.Size
}
}
Drive Size (MB) Free Space (MB) PercentFree
----- --------- --------------- -----------
C: 960550,99609375 55243,57421875 5,75123803352531
By abusing the [int] type, you can round to the whole number:
[Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' |
ForEach-Object {
[PSCustomObject]@{
Drive = $_.DeviceID
'Size (MB)' = $_.Size/1MB -as [int]
'Free Space (MB)' = $_.FreeSpace/1MB -as [int]
PercentFree = $_.FreeSpace * 100 / $_.Size -as [int]
}
}
Drive Size (MB) Free Space (MB) PercentFree
----- --------- --------------- -----------
C: 960551 55239 6
With [Math] and Round(), you can specify the number of digits for the result that works best for you:
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' |
ForEach-Object {
[PSCustomObject]@{
Drive = $_.DeviceID
'Size (MB)' = [Math]::Round( ($_.Size/1MB), 1)
'Free Space (MB)' = [Math]::Round( ($_.FreeSpace/1MB), 2)
PercentFree = [Math]::Round( ($_.FreeSpace * 100 / $_.Size), 1)
}
}
Drive Size (MB) Free Space (MB) PercentFree
----- --------- --------------- -----------
C: 960551 55238,11 5,8
When you look closely, you’ll see that the total size is reported as a number with no digits, even though we asked for one digit. That’s because Round() always returns numbers, not formatted strings.
If the digit happens to be "0," then PowerShell’s console formatter won’t display it:
PS C:\> 1.0
1
Just for completeness, you could also use the -f operator to round values. Because this operator always returns strings, the digit would now always be visible, even if it happens to be "0."
However, since the values are now strings, this would be useful for reporting purposes only. Anyone receiving the data would have a hard time evaluating the string numbers further:
GGet-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' |
ForEach-Object {
[PSCustomObject]@{
Drive = $_.DeviceID
'Size (MB)' = '{0:n1}' -f ($_.Size/1MB)
'Free Space (MB)' = '{0:n2}' -f ($_.FreeSpace/1MB)
PercentFree = '{0:n1}' -f ($_.FreeSpace * 100 / $_.Size)
}
}
Drive Size (MB) Free Space (MB) PercentFree
----- --------- --------------- -----------
C: 960.551,0 55.253,95 5,8
Unleash the full potential of PowerShell with our handy poster. Whether you're a beginner or a seasoned pro, this cheat sheet is designed to be your go-to resource for the most important and commonly used cmdlets.
The poster is available for download and in paper form.
Apr 16, 2025 by Aleksandar Nikolić and Dr. Tobias Weltner
In Part 1, we explored the internal PowerShell parser to see how it analyzes code and breaks it down into individual...
Apr 11, 2025 by Aleksandar Nikolić and Dr. Tobias Weltner
The internal PowerShell parser processes any code before execution. With full access to this parser, you can analyze...
Apr 3, 2025 by Aleksandar Nikolić and Dr. Tobias Weltner
In part 3, we identified a useful .NET method to display system dialogs and then wrapped it inside a new PowerShell...
Tobias Weltner and Aleksandar Nikolić joinly wrote the blog post series 'Tobias&Aleksandar's PowerShell tips'. So we introduce both of them here:
----------------------------
Aleksandar Nikolić is a Microsoft Azure MVP and co-founder of PowerShellMagazine.com, the ultimate online source for PowerShell enthusiasts. With over 18 years of experience in system administration, he is a respected trainer and speaker who travels the globe to share his knowledge and skills on Azure, Entra, and PowerShell. He has spoken at IT events such as Microsoft Ignite, ESPC, NIC, CloudBrew, NTK, and PowerShell Conference Europe.
----------------------------
Tobias is a long-time Microsoft MVP and has been involved with the development of PowerShell since its early days. He invented the PowerShell IDE "ISESteroids", has written numerous books on PowerShell for Microsoft Press and O'Reilly, founded the PowerShell Conference EU (psconf.eu), and is currently contributing to the advancement of PowerShell as member in the "Microsoft Cmdlet Working Group". Tobias shares his expertise as a consultant in projects and as a trainer in in-house trainings for numerous companies and agencies across Europe.