2

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?

5
  • 3
    the range operator [the double dot] ONLY allows integers with a max value of 32k. i think the only way to do it will require a -ge and a -le compound test. Commented Dec 29, 2019 at 9:39
  • Yes, I was considering that as well. However, I am having a hard time then building the command line properly: Do { $value = Read-host "Specify a value between 23.976 and 60"} while ( $value -ge 23.976 ) -and ( $value -lt 60 ) ...doesn't seem to work Commented Dec 29, 2019 at 12:44
  • two problems - [1] your ENTIRE while test needs to be in one parenthesis pair. you have it broken into two. wrap the whole thing in another paren pair. [2] your while test is backwards. as it stands the only accepted answers will be OUTSIDE the range. you want the test to validate IN the range, so the "keep doing it until you get it right" test needs to be "while OUTSIDE of the target range. [grin] Commented Dec 29, 2019 at 13:48
  • 2
    third glitch - your $value is text. you likely will need to convert the digit text to a number. i would use [float]. [grin] Commented Dec 29, 2019 at 13:55
  • @Lee_Dailey: Good point about -ge and -le and generally needing to convert Read-Host output to a number for numerical comparison ([double] is probably preferable). The fundamental problem here is the attempt to use a .. range for checking a non-integer number range, but the while condition syntax and the logic are otherwise correct (the outside-the-range logic is in the -notcontains). As for the .. operator: The min. / max. endpoint values are [int]::MinValue and [int]::MaxValue, and, additionally, the resulting array must not have more than 2,146,435,071 elements. Commented Dec 30, 2019 at 14:05

3 Answers 3

2

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)

    • e.g., 1..3 creates array 1, 2, 3
    • Note: While the minimum and maximum endpoint values allowed are [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").

    • e.g., '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.

Sign up to request clarification or add additional context in comments.

Comments

0
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

1 Comment

Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?
-1

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 )) 

2 Comments

Didn't you see Lee's comment about the [float] ?
To illustrate why this is broken: $value = '9'; $value -gt 60 yields $true; prior conversion of the always-string Read-Host output to a number is necessary.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.