1

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?

TimZed
  • 11
  • 1
  • 4
  • 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. – Lee_Dailey Dec 29 '19 at 09: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 – TimZed Dec 29 '19 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*] – Lee_Dailey Dec 29 '19 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*] – Lee_Dailey Dec 29 '19 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_. – mklement0 Dec 30 '19 at 14:05

2 Answers2

1

PowerShell's range operator - .. - generates an array of discrete values 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))
mklement0
  • 382,024
  • 64
  • 607
  • 775
0

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 )) 
TimZed
  • 11
  • 1
  • 4