42

In PowerShell, how can I test if a variable holds a numeric value?

Currently, I'm trying to do it like this, but it always seems to return false.

add-type -Language CSharpVersion3 @'
    public class Helpers {
        public static bool IsNumeric(object o) {
            return o is byte  || o is short  || o is int  || o is long
                || o is sbyte || o is ushort || o is uint || o is ulong
                || o is float || o is double || o is decimal
                ;
        }
    }
'@

filter isNumeric($InputObject) {
    [Helpers]::IsNumeric($InputObject)
}

PS> 1 | isNumeric
False
Damian Powell
  • 8,655
  • 7
  • 48
  • 58

15 Answers15

57

You can check whether the variable is a number like this: $val -is [int]

This will work for numeric values, but not if the number is wrapped in quotes:

1 -is [int]
True
"1" -is [int]
False
tophatsteve
  • 984
  • 1
  • 9
  • 22
42

If you are testing a string for a numeric value then you can use the a regular expression and the -match comparison. Otherwise Christian's answer is a good solution for type checking.

function Is-Numeric ($Value) {
    return $Value -match "^[\d\.]+$"
}

Is-Numeric 1.23
True
Is-Numeric 123
True
Is-Numeric ""
False
Is-Numeric "asdf123"
False
joshuapoehls
  • 32,695
  • 11
  • 50
  • 61
  • 1
    I'm not testing a string, I'm testing a type. But ignoring that for the moment, how about negative values? And what about exponential values (3.24643e4)? Yeah, I know, I'm splitting hairs! – Damian Powell Jun 08 '12 at 14:42
  • 2
    I'll leave that as an exercise for the reader. :) I just wanted to add this for people who may stumble on the question looking for checking numeric string values. – joshuapoehls Jun 08 '12 at 15:10
  • 1
    Well done @silent__thought, I was looking for exactly this. – Tim Meers Aug 10 '16 at 18:17
  • `"1.2.3" -match "^[\d\.]+$"` --> True. `"^(\d+|\.\d+|\d+\.\d+)$"` would allow only one decimal separator. Returns true for "1", ".1", "1.1" and false for "1.", "1.1.1", "". – jumxozizi Jul 06 '18 at 12:51
  • this regex is incorrect, `Is-Numeric .................` would be true – Santiago Squarzon Jan 18 '23 at 00:01
33

You can do something like :

$testvar -match '^[0-9]+$'

or

$testvar -match '^\d+$'

Returns True if $testvar is a number.

Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
Matt
  • 331
  • 3
  • 3
31

Modify your filter like this:

filter isNumeric {
    [Helpers]::IsNumeric($_)
}

function uses the $input variable to contain pipeline information whereas the filter uses the special variable $_ that contains the current pipeline object.

Edit:

For a powershell syntax way you can use just a filter (w/o add-type):

filter isNumeric($x) {
    return $x -is [byte]  -or $x -is [int16]  -or $x -is [int32]  -or $x -is [int64]  `
       -or $x -is [sbyte] -or $x -is [uint16] -or $x -is [uint32] -or $x -is [uint64] `
       -or $x -is [float] -or $x -is [double] -or $x -is [decimal]
}
ndemou
  • 4,691
  • 2
  • 30
  • 33
CB.
  • 58,865
  • 9
  • 159
  • 159
17

If you want to check if a string has a numeric value, use this code:

$a = "44.4"
$b = "ad"
$rtn = ""
[double]::TryParse($a,[ref]$rtn)
[double]::TryParse($b,[ref]$rtn)

Credits go here

tsap
  • 803
  • 8
  • 10
12
PS> Add-Type -Assembly Microsoft.VisualBasic
PS> [Microsoft.VisualBasic.Information]::IsNumeric(1.5)
True

http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.information.isnumeric.aspx

Ocaso Protal
  • 19,362
  • 8
  • 76
  • 83
x0n
  • 51,312
  • 7
  • 89
  • 111
  • 1
    I thought this was pretty neat until I realised that it would return true for a string that was numeric such as `"1.23"` which is not what I want. I want to know if the type of the actual object is a numeric type, not whether or not it can be coerced to a numeric type. Thanks for a useful tip though, I can see where that will be very handy. – Damian Powell Jun 07 '12 at 17:12
10

-is and -as operators requires a type you can compare against. If you're not sure what the type might be, try to evaluate the content (partial type list):

(Invoke-Expression '1.5').GetType().Name -match 'byte|short|int32|long|sbyte|ushort|uint32|ulong|float|double|decimal'

Good or bad, it can work against hex values as well (Invoke-Expression '0xA' ...)

Shay Levy
  • 121,444
  • 32
  • 184
  • 206
  • That's a neat solution. For the context I need to use it though, `Invoke-Expression` could be quite dodgy! – Damian Powell Jun 08 '12 at 14:48
  • 2
    Try{(Invoke-Expression '-123s.456e-789 ').GetType().Name -match 'byte|short|int32|long|sbyte|ushort|uint32|ulong|float|double|decimal'}Catch{$false}---Although you solution is almost perfect and flexible – Garric Aug 30 '20 at 04:50
6
filter isNumeric {
    $_ -is [ValueType]
}

-

1 -is [ValueType]
True
"1" -is [ValueType]
False

-

function isNumeric ($Value) {
    return $Value -is [ValueType]
}

isNumeric 1.23
True
isNumeric 123
True
isNumeric ""
False
isNumeric "asdf123"
False

-

(Invoke-Expression '1.5') -is [ValueType]
Aaron
  • 374
  • 3
  • 8
4
$itisint=$true
try{
 [int]$vartotest
}catch{
 "error converting to int"
 $itisint=$false
}

this is more universal, because this way you can test also strings (read from a file for example) if they represent number. The other solutions using -is [int] result in false if you would have "123" as string in a variable. This also works on machines with older powershell then 5.1

McVitas
  • 272
  • 1
  • 17
  • 2
    It’s always valuable to explain your code, but that’s especially true in a case like this where there’s a highly-rated accepted answer from eight years ago. Why is your approach preferred over the accepted answer? Do you mind updating your answer with more details? – Jeremy Caney May 22 '20 at 05:44
  • 2
    If you are using `Read-Host` the result is always a string so you need to perform a check to see if the input even *is* numeric before converting. Generating and catching exceptions as flow control is always bad due to the amount of stuff done behind the scenes. The powershell way would be `$converted = $myInput -as [int]`. `$converted` will be `$null` if it could not convert, or the number if it was successful – DreadedEntity Mar 05 '22 at 18:08
3

Thank you all who contributed to this thread and helped me figure out how to test for numeric values. I wanted to post my results for how to handle negative numbers, for those who may also find this thread when searching...

Note: My function requires a string to be passed, due to using Trim().

function IsNumeric($value) {
# This function will test if a string value is numeric
#
# Parameters::
#
#   $value   - String to test
#
   return ($($value.Trim()) -match "^[-]?[0-9.]+$")
}
edwdecarlo
  • 47
  • 3
  • 7
    This will return true for version numbers like "9.3.4", you'd be better served by a regex like `^[-+]?([0-9]*\.[0-9]+|[0-9]+\.?)$` which will validate you have only an optional plus/minus; numbers and an single optional decimal point. – Ro Yo Mi Apr 03 '14 at 20:39
3

If you know the numeric type you want to test against (such as int for example in the code below), you can do it like this:

> [bool]("42" -as [int])
True
> [bool](42 -as [int])
True
> [bool]("hi" -as [int])
False

But note:

> [bool](42.1 -as [int])
True

Careful!:

It was pointed out that the code above fails to identify 0 as an int. You would need to add a guard for 0:

> $n -eq 0 -or $n -as [int]

Where $n is the object you are testing.

dan-gph
  • 16,301
  • 12
  • 61
  • 79
2

I ran into this topic while working on input validation with read-host. If I tried to specify the data type for the variable as part of the read-host command and the user entered something other than that data type then read-host would error out. This is how I got around that and ensured that the user enters the data type I wanted:

do
    {
    try
        {
        [int]$thing = read-host -prompt "Enter a number or else"
        $GotANumber = $true
        }
    catch
        {
        $GotANumber = $false
        }
    }
until
    ($gotanumber)
James
  • 21
  • 1
1
"-123.456e-789" -match "^\-?(\d+\.?\d*)(e\-?\d+)?$|^0x[0-9a-f]+$"

or

"0xab789" -match "^\-?(\d+\.?\d*)(e\-?\d+)?$|^0x[0-9a-f]+$"

will check for numbers (integers, floats and hex).

Please note that this does not cover the case of commas/dots being used as separators for thousands.

1

Each numeric type has its own value. See TypeCode enum definition: https://learn.microsoft.com/en-us/dotnet/api/system.typecode?view=netframework-4.8 Based on this info, all your numeric type-values are in the range from 5 to 15. This means, you can write the condition-check like this:

$typeValue = $x.getTypeCode().value__
if ($typeValue -ge 5 -and $typeValue -le 15) {"x has a numeric type!"}
Carsten
  • 1,612
  • 14
  • 21
0

Testing if a value is numeric or a string representation of a numeric value.

function Test-Number 
{
    Param
    (
        [Parameter(Mandatory=$true,
                   Position=0)]
        [ValidatePattern("^[\d\.]+$")]
        $Number
    )

    $Number -is [ValueType] -or [Double]::TryParse($Number,[ref]$null)
}

Testing if a value is numeric.

function Test-Number 
{
    Param
    (
        [Parameter(Mandatory=$true,
                   Position=0)]
        [ValidatePattern("^[\d\.]+$")]
        $Number
    )

    $Number -is [ValueType]
}
Vince Ypma
  • 65
  • 5
  • 1
    `$n=0; [Double]::TryParse('1e2', [ref]$n)` will parse it as a string representation of a number, but your functions will fail it. Same with negative numbers. – TessellatingHeckler Feb 18 '17 at 03:24