To complement Sean’s helpful answer:
It is only the type constraint of your result variable ([uint64] $ConvertedMemory
= …) that ensures that ($MemoryFromString / 1)
is converted to [uint64]
([System.UInt64]
).
The result of expression $MemoryFromString / 1
is actually of type [int]
([System.Int32]
):
PS> ('1gb' / 1).GetType().FullName
System.Int32
Therefore, to ensure that the expression by itself returns an [uint64]
instance, you’d have to use a cast:
PS> ([uint64] ('1gb' / 1)).GetType().FullName
System.Int64
Note the required (...)
around the calculation, as the [uint64]
cast would otherwise apply to '1gb'
only (and therefore fail).
Alternatively, ('1gb' / [uint64] 1)
works too.
Note:
'1gb' - 0
would have worked too,- but not
'1gb' * 1'
(effectively a no-op) or'1gb' + 0
(results in string'1gb0'
), because operators*
and+
with a string-typed LHS perform string operations (replication and concatenation, respectively).
Automatic string-to-number conversion and number literals in PowerShell:
When PowerShell performs implicit number conversion, including when performing mixed-numeric-type calculations and parsing number literals in source code, it conveniently auto-selects a numeric type that is “large” enough to hold the result.
In implicit string-to-number conversions, PowerShell conveniently recognizes the same formats as supported in number literals in source code:
-
number-base prefixes (for integers only):
0x
for hexadecimal integers, and0b
for binary integers (PowerShell [Core] 7.0+) -
number-type suffixes:
L
for[long]
([System.Int64]
), andD
for[decimal]
([System.Decimal]
); e.g.,'1L' - 0
yields a[long]
.
Note that C# usesM
instead ofD
and instead usesD
to designate[System.Double]
; also, C# supports several additional suffixes.-
PowerShell [Core] 6.2+ now supports additional suffixes:
Y
([sbyte]
),UY
([byte]
),S
([int16]
),US
([uint16]
),U
([uint32]
or[uint64]
, on demand), andUL
([uint64]
). -
PowerShell [Core] 7.0+ additionally suports suffix
n
([bigint]
) -
You can keep an eye on future developments, if any, via the official help topic, about_Numeric_Literals.
-
-
floating-point representations such as
1.23
(decimal only); note that PowerShell only ever recognizes.
as the decimal mark, irrespective of the current culture. -
exponential notation (decimal only); e.g.,
'1.0e3' - 1
yields999
. -
its own binary-multiplier suffixes,
kb
,mb
,gb
,tb
,pb
(for multipliers[math]::pow(2, 10)
==1024
,[math]::pow(2, 20)
==1048576
, …); e.g.,'1kb' - 1
yields1023
; note that theses suffixes are PowerShell-specific, so the .NET framework number-parsing methods do not recognize them.
The number-conversion rules are complex, but here are some key points:
This is based on my own experiments. Do tell me if I’m wrong.
Types are expressed by their PS type accelerators and map onto .NET types as follows:
[int]
… [System.Int32]
[long]
… [System.Int64]
[decimal]
… [System.Decimal]
[float]
… [System.Single]
[double]
… [System.Double]
-
PowerShell never auto-selects an unsigned integer type.
- Note: In PowerShell [Core] 6.2+, you can use type suffix
US
,U
orUL
(see above) to force treatment as an unsigned type (positive number); e.g.,0xffffffffffffffffU
- This can be unexpected with hexadecimal number literals; e.g.,
[uint32] 0xffffffff
fails, because0xffffffff
is first – implicitly – converted to signed type[int32]
, which yields-1
, which, as a signed value, cannot then be cast to unsigned type[uint32]
. - Workarounds:
- Append
L
to force interpretation as an[int64]
first, which results in expected positive value4294967295
, in which case the cast to[uint32]
succeeds. - That technique doesn’t work for values above
0x7fffffffffffffff
([long]::maxvalue
), however, in which case you can use string conversion:[uint64] '0xffffffffffffffff'
- Append
- Note: In PowerShell [Core] 6.2+, you can use type suffix
-
PowerShell widens integer types as needed:
-
For decimal integer literals / strings, widening goes beyond integer types to
[System.Decimal]
, and then[Double]
, as needed; e.g.:-
(2147483648).GetType().Name
yieldsInt64
, because the value is[int32]::MaxValue + 1
, and was therefore implicitly widened to[int64]
. -
(9223372036854775808).GetType().Name
yieldsDecimal
, because the value is[int64]::MaxValue + 1
, and was therefore implicitly widened to[decimal]
. -
(79228162514264337593543950336).GetType().Name
yieldsDouble
, because the value is[decimal]::MaxValue + 1
, and was therefore implicitly widened to[double]
.
-
-
For hexadecimal (invariably integer) literals / strings, widening stops at
[int64]
:-
(0x100000000).gettype().name
yieldsInt64
, because the value is[int32]::MaxValue + 1
, and was therefore implicitly widened to[int64]
. -
0x10000000000000000
, which is[int64]::MaxValue + 1
, does not get promoted to[System.Decimal]
due to being hexadecimal and interpretation as a number therefore fails.
-
-
Note: The above rules apply to individual literals / strings, but widening in expressions may result in widening to
[double]
right away (without considering[decimal]
) – see below.
-
-
PowerShell seemingly never auto-selects an integer type smaller than
[int]
:('1' - 0).GetType().FullName
yieldsSystem.Int32
(an[int]
), even though integer1
would fit into[int16]
or even[byte]
.
-
The result of a calculation never uses a smaller type than either of the operands:
- Both
1 + [long] 1
and[long] 1 + 1
yield a[long]
(even though the result could fit into a smaller type).
- Both
-
Perhaps unexpectedly, PowerShell auto-selects floating-point type
[double]
for a calculation result that is larger than either operand’s type integer type can fit, even if the result could fit into a larger integer type:([int]::maxvalue + 1).GetType().FullName
yieldsSystem.Double
(a[double]
), even though the result would fit into a[long]
integer.- If one of the operands is a large-enough integer type, however, the result is of that type:
([int]::maxvalue + [long] 1).GetType().FullName
yieldsSystem.Int64
(a[long]
).
-
Involving at least one floating-point type in a calculation always results in
[double]
, even when mixed with an integer type or using all-[float]
operands:1 / 1.0
and1.0 / 1
and1 / [float] 1
and[float] 1 / 1
and[float] 1 / [float] 1
all yield a[double]
-
Number literals in source code that don’t use a type suffix:
-
Decimal integer literals are interpreted as the smallest of the following types that can fit the value:
[int]
>[long]
>[decimal]
>[double]
(!):1
yields an[int]
(as stated,[int]
is the smallest auto-selected type)214748364
(1 higher than[int]::maxvalue
) yields a[long]
9223372036854775808
(1 higher than[long]::maxvalue
) yields a[decimal]
79228162514264337593543950336
(1 higher than[decimal]::maxvalue
) yields a[double]
-
Hexadecimal integer literals are interpreted as the smallest of the following types that can fit the value:
[int]
>[long]
; that is, unlike with decimal literals, types larger than[long]
aren’t supported; Caveat: values that have the high bit set result in negative decimal numbers, because PowerShell auto-selects signed integer types:-
0x1
yields an[int]
-
0x80000000
yields an[int]
that is a negative value, because the high bit is set:-2147483648
, which is the smallest[int]
number, if you consider the sign ([int]::MinValue
) -
0x100000000
(1 more than can fit into an[int]
(or[uint32]
)) yields a[long]
-
0x10000000000000000
(1 more than can fit into a[long]
(or[uint64]
)) breaks, because[long]
is the largest type supported (“the numeric constant is not valid”). -
To ensure that a hexadecimal literal results in a positive number:
-
Windows PowerShell: Use type suffix
L
to force interpretation as a[long]
first, and then (optionally) cast to an unsigned type; e.g.[uint32] 0x80000000L
yields2147483648
, but note that this technique only works up to0x7fffffffffffffff
, i.e.,[long]::maxvalue
; as suggested above, use a conversion from a string as a workaround (e.g.,[uint64] '0xffffffffffffffff'
). -
PowerShell [Core] 6.2+: Use type suffix
us
,u
, orul
, as needed; e.g.:0x8000us
->32768
([uint16]
),0x80000000u
->2147483648
([uint32]
),0x8000000000000000ul
->9223372036854775808
([uint64]
)
-
-
-
Binary integer literals (PowerShell [Core] 7.0+) are interpreted the same way as hexadecimal ones; e.g.,
0b10000000000000000000000000000000
==0x80000000
==-2147483648
([int]
) -
Floating-point or exponential notation literals (which are only recognized in decimal representation) are always interpreted as a
[double]
, no matter how small:1.0
and1e0
both yield a[double]
-