Sign up for our newsletter.

I agree to this site's Privacy Policy.

Prof. Powershell

Math Matters

Hate math? Hate it no longer when you invoke the .NET Math class for this week's data formatting tip.

I'm sure many of you have either seen code like this if not run it yourself:

PS C:\> Get-WmiObject win32_logicaldisk -filter "drivetype=3" | Select DeviceId,Size,Freespace

DeviceId                   Size        Freespace
--------                   ----        ---------
C:                 201504845824      134099279872
D:                  32499560448        5596352512
E:                    209711104         135725056

You've probably also tried to reformat the number to something more meaningful, such as megabytes:

PS C:\> Get-WmiObject win32_logicaldisk -filter "drivetype=3" | Select DeviceID,@{Name="SizeMB";Expression={$_.Size/1MB}},@{Name="FreeMB";Expression={$_.Freespace/1MB}}

DeviceID                 SizeMB           FreeMB
--------                 ------           ------
C:              192169.99609375   127881.6796875
D:               30993.99609375    5337.09765625
E:                 199.99609375         129.4375

Of course very few people like all those decimal points. So you might try shortening them with this:

PS C:\> Get-WmiObject win32_logicaldisk -filter "drivetype=3" | Select DeviceID,@{Name="SizeMB";Expression={"{0:N2}" -f ($_.Size/1MB)}},
@{Name="FreeMB";Expression={"{0:N2}" -f ($_.Freespace/1MB)}}

DeviceID       SizeMB          FreeMB
--------       ------          ------
C:             192,170.00      127,887.63
D:             30,994.00       5,337.10
E:             200.00          129.44

This certainly looks better and if all you want is a simple formatted report, this is fine. But what you see is not what you get. Look at this:

PS C:\> Get-WmiObject win32_logicaldisk -filter "drivetype=3" |
Select DeviceID,@{Name="SizeMB";Expression={"{0:N2}" -f ($_.Size/1MB)}},
@{Name="FreeMB";Expression={"{0:N2}" -f ($_.Freespace/1MB)}} | Sort FreeMB -Descending

DeviceID       SizeMB          FreeMB
--------       ------          ------
D:             30,994.00       5,337.10
E:             200.00          129.44
C:             192,170.00      127,880.50

It's clearly not sorting in the expected order. That's because when you use the -f operator, it creates a string value. If you had a numeric value property from many machines you won't get the results you expect.

One solution is to convert this string back into a number:

PS C:\> Get-WmiObject win32_logicaldisk -filter "drivetype=3" |
Select DeviceID,@{Name="SizeMB";Expression={("{0:N2}" -f ($_.Size/1MB)) -as [double]}},@{Name="FreeMB";Expression={("{0:N2}" -f ($_.Freespace/1MB)) -as [double]}} | Sort FreeMB -Descending

DeviceID         SizeMB         FreeMB
--------         ------         ------
C:               192170      127881.47
D:                30994         5337.1
E:                  200         129.44

That's more like it, but it sure seems like a lot of work. Here's a better way using the .NET Math class.

There are no math cmdlets, but you can access the math library by using [Math]. You can't create a math object, but once you know the method, you can simply invoke it. Here's a one-liner to help show you the methods:

([math]).GetMethods() | sort name | Select Name -unique

Once you know the method, then you can run commands like this:

PS C:\> [math]::Sqrt(25)

PS C:\> [math]::Pow(2,10)

PS C:\> [math]::Round(123.45678,2)

It's that last method that we're interested in. The method needs a number and number of decimal places. The best part is that it writes a numeric object, technically a double in this example, to the pipeline. Thus, we can modify our WMI query accordingly:

PS C:\> Get-WmiObject win32_logicaldisk -filter "drivetype=3" |
Select DeviceID,@{Name="SizeMB";Expression={[math]::Round($_.Size/1MB,2)}},
@{Name="FreeMB";Expression={[math]::Round($_.Freespace/1MB,2)}} | Sort FreeMB -Descending

DeviceID        SizeMB         FreeMB
--------        ------         ------
C:              192170      127884.68
D:               30994         5337.1
E:                 200         129.44

I haven't found a good and easy way to force PowerShell to include the trailing 0, but I can live with this. If you don't want any decimal points, simply eliminate the second parameter value in the Round() method. But now I have a way to get numeric data in the format I want without sacrificing anything.

comments powered by Disqus

Reader Comments:

Thu, Oct 17, 2013 Delia Sunsripirgly well-written and informative for a free online article.

http://d DOT well-written and informative for a free online article.

Fri, Sep 27, 2013 Issy cheap business insurance DOT

Mon, Sep 24, 2012 JEFFERY HICKS

Well, this is more a formatting question than using [math]. But sure this will format the number. "{0:N}" -f 0012345.67 Depending on the bigger picture you could use the padleft method. ("{0:N}" -f $x).Padleft(20) Or you could use a custom property with Format-Table setting the alignment.

Wed, Sep 19, 2012 Dave Ohio

Nice examples Thanks! Here's the frosting on the cake for accountant types. Can one take the integer and present the object: [1] right-aligned, [2] eliminate leading Zeros and [3] comma delimit the object ... like money for example, but [4] without the '$' dollar sign that the 'C' format for currency carries? Thanks again!

Add Your Comment Now:

Your Name:(optional)
Your Email:(optional)
Your Location:(optional)
Please type the letters/numbers you see above