This is an example, the real thing is a large New-Object 'object[,]'
2D array:
PS C:\> $a=[int16]10
PS C:\> $a.GetType().Name
Int16
PS C:\> $a
PS C:\> $a.GetType().Name
Int32
PS C:\> $a=[int16]10
PS C:\> $a.GetType().Name
Int16
PS C:\> $a=$a [int16]1
PS C:\> $a.GetType().Name
Int32
I can FORCE the type in such things, but this is actually a conversion after the math is done and slows down the the complete code by ~ 10%, so it is useless:
PS C:\> $a=[int16]10
PS C:\> $a.GetType().Name
Int16
PS C:\> $a=[int16]($a 1)
PS C:\> $a.GetType().Name
Int16
Is there no way to prevent this auto-conversion from byte and int16 to it's beloved int32 when using any math?
As for the comment of @mklement0 I extend the examples to the use case. Note that no value anywhere on that board exceeds "8":
# Init board
$BoardXSize = [int]100
$BoardYSize = [int]100
$Board = New-Object 'object[,]' $BoardXSize,$BoardYSize
for ($y=0;$y -lt $BoardYSize;$y ) {
for ($x=0;$x -lt $BoardXSize;$x ) {
[int16]$Board[$x,$y] = 0
}
}
Testing:
PS C:\> ($Board[49,49]).GetType().Name
Int16
PS C:\> $Board[49,49]
PS C:\> ($Board[49,49]).GetType().Name
Int32
PS C:\> [int16]$Board[49,49]=0
PS C:\> ($Board[49,49]).GetType().Name
Int16
PS C:\> [int16]$Board[49,49]
0
PS C:\> ($Board[49,49]).GetType().Name
Int32
PS C:\> [int16]$Board[49,49]=0
PS C:\> ($Board[49,49]).GetType().Name
Int16
PS C:\> $Board[49,49] = [int16]1
PS C:\> ($Board[49,49]).GetType().Name
Int32
PS C:\> [byte]$Board[49,49]=0
PS C:\> ($Board[49,49]).GetType().Name
Byte
PS C:\> $Board[49,49] = [byte]1
PS C:\> ($Board[49,49]).GetType().Name
Int32
However when being EXTREMELY PICKY about the operators it works, just " " seems to fail:
PS C:\> [int16]$Board[49,49]=0
PS C:\> ($Board[49,49]).GetType().Name
Int16
PS C:\> [int16]$Board[49,49] = [int16]1
PS C:\> ($Board[49,49]).GetType().Name
Int16
PS C:\> [byte]$Board[49,49]=0
PS C:\> ($Board[49,49]).GetType().Name
Byte
PS C:\> [byte]$Board[49,49] = [byte]1
PS C:\> ($Board[49,49]).GetType().Name
Byte
But since this is always a cast after the math was done it does not improve speed. But it is solved.
CodePudding user response:
You can type-constrain your variable, by placing the type literal to the left of the (initial) assignment:
# Note how [int16] is to the *left* of $a = ...
PS> [int16] $a = 10; $a; $a = 1; $a, $a.GetType().Name
12
Int16
Note that this works differently than typing a variable in a statically typed language such as C#, for example: Instead of locking in the specified type statically, a cast is performed on every assignment in order to coerce the value being assigned to that type.
For instance, with [int16] $a = 10
having been the original assignment, $a = 1
is effectively the same as assigning [int16] ($a 1)
.
This also implies that you're free to assign values of any type, as long as they can be converted to the constraining type; e.g.:
# These work, because they are implicitly cast (converted) to [int16]
$a = 1.2
$a = '42'
However, note that type-constraining only works with variables, not with so-called L-values (assignment targets) in general.
That is, your example of [int16]$Board[$x,$y] = 0
does not work, because the attempt to type-constraint is quietly ignored in this case; it is solely the type of the $Board
array as originally declared that determines the elements' type.
See the next section about arrays.
Applying type constraints to arrays:
To a (one-dimensional) array:
# Strongly types the array as storing [int16] instances.
PS> [int16[]] $arr = 10, 11; $arr[0]; $arr[1] = 1; $arr; $arr.ForEach('GetType').Name
11
12
Int16
Int16
To a two-dimensional array (rare in PowerShell):
# Strongly types the 2D array as storing [int16] instances.
PS> $countDim1, $countDim2 = 2, 3;
$arr2d = [int16[,]]::new($countDim1, $countDim2);
$arr2d[0,0]; $arr2d[0,0].GetType().Name
Int16
Note that this is truly creates strongly, statically typed .NET arrays, but assigning to their elements again applies PowerShell's flexible type conversions (e.g., $arr[0] = '42'
works fine).
Also note that the 2D array example, for brevity, doesn't actually type-constrain the data type of the variable, given that there's no type literal to the left of the assignment; therefore, you could still replace the entire array with an arbitrary new value of any type (e.g., $arr2d = 'foo'
); to perform type-constraining too, you'd have to do:
# Creates a 2D [int16] array *and* type-constrains $arr2D to
# such arrays.
[int16[,]] $arr2d = [int16[,]]::new($countDim1, $countDim2)
Also unlike languages such as C#, PowerShell automatically widens types in numerical operations.
This widening apples not just to operands of differing (numeric) types, but also if the result would be too large to fit into the (larger of the two) input type(s), and it has two surprising aspects:
A minimum width of
[int]
(System.Int32
) is applied, which means that types smaller than[int]
are always widened to[int]
in the result, even if it isn't necessary, as you've observed:# Implicitly widens to [int], even though not necessary. # Note that the [byte] cast is only applied to the LHS PS> ([byte] 42 1).GetType().Name Int32
If the result is too large to fit into the (larger of the two) input type(s), the result is invariably widened to a
[double]
(System.Double
)(!)# Implicitly widens to [double](!), because adding 1 # to the max. [int] value doesn't fit into an [int]. PS> ([int]::MaxValue 1).GetType().Name Double # NO widening, because the large input type is a [long] (System.Int64) # (suffix L), so the result fits into a [long] too. PS> ([long] [int]::MaxValue 1).GetType().Name Int64
This immediate widening to
[double]
in expressions, without considering larger integer types, differs from how number literals are parsed in PowerShell, where larger integer types are used (but note that PowerShell never chooses unsigned types):# 2147483648 = [int]::MaxValue 1, so [long] (System.Int64) # is chose as the literal's type. (2147483648).GetType().Name In64
- Note that number literals can also have type suffixes to explicitly designate their type, such as
l
for[long]
andd
for[decimal]
; e.g.,2147483648d.GetType().Name
returnsDecimal
.
- Note that number literals can also have type suffixes to explicitly designate their type, such as