Reputation: 29
I have seen this here for different scripting languages, however I have my problem in Powershell and as follows: I want to limit the input of a numeric value, which can either be a decimal or an integer one, to a specific range.
I tried the following:
Do { $value = Read-host "Specify a value between 23.976 and 60"}
while ((23.976..60) -notcontains $value )
However it seems that like this it doesn't accept any decimal values at all but only integers. For instance, when I try to enter 29.97 or 29.970 it stays in the loop.
How can I do this right?
Upvotes: 2
Views: 5167
Reputation: 437953
PowerShell's range operator - ..
- generates an array of discrete values[1] from the range endpoints, which can have the following types (in a given expression, both endpoints must have the same type):
[int]
(System.Int32
)
1..3
creates array 1, 2, 3
[int]::MinValue
and [int]::MaxValue
, respectively, the resulting array must additionally not have more than 2,146,435,071
elements (2+ billion), which is the max. element count for a single-dimensional [int]
array in .NET - see this answer for background information.Alternatively, only in PowerShell [Core, 6+]: characters, which are enumerated between the endpoints by their underlying code points ("ASCII values").
'a'..'c'
creates array [char] 'a', [char] 'b', [char] 'c'
Instances of numeric types other than [int]
are quietly coerced to the latter - if they can fit into the [int]
range - in which case half-to-even midpoint rounding is performed for non-integral types; e.g., implicit [double]
instances 23.5
and 24.5
are both coerced to 24
.
Because [int] 23.976
is 24
, your 23.976..60
expression creates array 24, 25, 26, ..., 60
which is not your intent.
In short: You cannot use ..
to describe an uncountable range of non-integers (fractional numbers).
Instead, use -ge
(greater than or equal) and -le
(less than or equal) to test against the endpoints:
-not ($value -ge 23.976 -and $value -le 60)
Additionally, in order to make the -ge
and -le
operations work as intended, convert the return value from Read-Host
, which is always a string, to a number.
If you were to use $value
as directly returned by Read-Host
, you'd get lexical (text sort order-based) comparison, not numeric comparison.
Therefore, cast the Read-Host
return value to [double]
:
$value = try { [double] (Read-Host 'Specify a value between 23.976 and 60') }
catch {}
Note: The try
/ catch
handles the case when the user enters text that cannot be interpreted as a [double]
representation.
To put it all together:
do {
$value = try { [double] (Read-Host 'Specify a value between 23.976 and 60') }
catch {}
} while (-not ($value -ge 23.976 -and $value -le 60))
[1] Strictly speaking, ..
creates a lazy enumerable that only becomes an [object[]]
array when enumeration is performed and the results are collected, such as when a ..
operation participates in a larger expression or it is captured in a variable.
Upvotes: 2
Reputation: 11
Function Get-NumberInRange {
try {
[ValidateRange(23.976,60)]$NumberInRange = Read-host "Specify a value between 23.976 and 60"
}
Catch {
Get-NumberInRange
}
return $NumberInRange
}
$Value = Get-NumberInRange
Upvotes: 0
Reputation: 29
OK guys, this is how it works ;)
Do { $value = Read-host "Specify a value between 23.976 and 60"}
while (( $value -gt 60 ) -or ( $value -lt 23.976 ))
Upvotes: -1