Reputation: 67
I am using powershell for some time now, and just stumbled upon the PSKoan Project:
https://github.com/vexx32/PSKoans
On one of the very first koans I found some strange behaviour, I cant explain to my self. The koan about number types try to teach how powershell will convert variable types dynamically from int to long and from int to double on certain operations.
So I filled out the blanks (as expected during the course) of this specific pester test:
It 'can be a larger number if needed' {
<#
Integers come in two flavours:
- int (Int32)
- long (Int64)
If an integer value exceeds the limits of the Int32 type, it is
automatically expanded to the larger Int64 type.
#>
# What exactly are the limitations of the [int] type?
$MaxValue = [int]::MaxValue
$MinValue = [int]::MinValue
2147483647 | Should -Be $MaxValue
-2147483648 | Should -Be $MinValue
# If you enter a number larger than that, the type should change.
$BigValue = $MaxValue +1
$BigValue | Should -BeOfType [long]
$BigValue | Should -BeGreaterThan $MaxValue
$SmallValue = $MinValue -1
$SmallValue | Should -BeOfType [long]
$SmallValue | Should -BeLessThan $MinValue
}
Use show-karma to let PSKoans to their magic:
The answers you seek... Expected the value to have type [long] or any of its subtypes, but got 2147483648 with type [double].
This can be narrowed down to the following example, which I dont understand:
Why the heck is this a double now, and not a long?
I am using PS 5.1:
P.S. Its late here, so perhaps I miss something really obvious, I am already prepared for the great face palm ;)
Upvotes: 2
Views: 380
Reputation: 438028
Indeed, in the context of expressions (calculations), PowerShell indeed automatically widens anything that exceeds the max. value of [int]
/ [uint]
(32-bit signed / unsigned integers) or [long]
/ [ulong]
(64-bit) to [double]
, which is easy to verify[1]:
# Ditto for [uint], [long], [ulong]
PS> ([int]::MaxValue + 1).GetType().Name
Double # [double]
By contrast, integer types smaller than [int]
are promoted to [int]
(System.Int32
) if their max. value is exceeded in a calculation:
# Ditto for [sbyte], [int16], [uint16]
PS> ([byte]::MaxValue + 1).GetType().Name
Int32 # same as: [int]
It is only with number literals that promotion to the next biggest - signed - integer type occurs for values beyond [int]
:
Note: Any number literal whose value is between [int]::MinValue
and [int]::MaxValue
- even if it could fit into a smaller integer type - defaults to [int]
, i.e., a 32-bit signed integer (System.Int32
).
# Note: 2147483648 is the result of ([int]::MaxValue + 1)
PS> (2147483648).GetType().Name
Int64 # same as: [long]
And if a number literal exceeds the value of [long]::MaxValue
, promotion to [decimal]
occurs:
# Note: 9223372036854775808 is the result of ([long]::MaxValue + 1)
PS> (9223372036854775808).GetType().Name
Decimal # [decimal]
It is only if you exceed [decimal]::MaxValue]
that promotion to [double]
- with its potential loss of precision - occurs:
# Note: 79228162514264337593543950336 is the result of ([decimal]::MaxValue + 1)
PS> (79228162514264337593543950336).GetType().Name
Double # [double]
Outputting the value above directly makes the conversion to [double]
immediately obvious, because the output formatting uses exponential notation: 7.92281625142643E+28
[1] Curiously, trying to calculate a value beyond [decimal]::MaxValue
fails, causing a statement-terminating error: ([decimal]::MaxValue + 1).GetType().Name
Upvotes: 3