Consider the following Powershell snippet:
[Uint64] $Memory = 1GB
[string] $MemoryFromString = "1GB"
[Uint64] $ConvertedMemory = [Convert]::ToUInt64($MemoryFromString)
The 3rd Line fails with:
Exception calling "ToUInt64" with "1" argument(s): "Input string was not in a correct format."
At line:1 char:1
+ [Uint64]$ConvertedMemory = [Convert]::ToUInt64($MemoryFromString)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : FormatException
If I check the contents of $Memory
:
PS C:\> $Memory
1073741824
That works fine.
So, how do I convert the value "1GB" from a string to a UInt64 in Powershell?
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,'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).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, and 0b
for binary integers (PowerShell [Core] 7.0+)
number-type suffixes: L
for [long]
([System.Int64]
), and D
for [decimal]
([System.Decimal]
); e.g., '1L' - 0
yields a [long]
.
Note that C# uses M
instead of D
and instead uses D
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), and UL
([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
yields 999
.
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
yields 1023
; 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.
US
, U
or UL
(see above) to force treatment as an unsigned type (positive number); e.g., 0xffffffffffffffffU
[uint32] 0xffffffff
fails, because 0xffffffff
is first - implicitly - converted to signed type [int32]
, which yields -1
, which, as a signed value, cannot then be cast to unsigned type [uint32]
.L
to force interpretation as an [int64]
first, which results in expected positive value 4294967295
, in which case the cast to [uint32]
succeeds.0x7fffffffffffffff
([long]::maxvalue
), however, in which case you can use string conversion: [uint64] '0xffffffffffffffff'
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
yields Int64
, because the value is [int32]::MaxValue + 1
, and was therefore implicitly widened to [int64]
.
(9223372036854775808).GetType().Name
yields Decimal
, because the value is [int64]::MaxValue + 1
, and was therefore implicitly widened to [decimal]
.
(79228162514264337593543950336).GetType().Name
yields Double
, 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
yields Int64
, 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
yields System.Int32
(an [int]
), even though integer 1
would fit into [int16]
or even [byte]
.The result of a calculation never uses a smaller type than either of the operands:
1 + [long] 1
and [long] 1 + 1
yield a [long]
(even though the result could fit into a smaller type).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
yields System.Double
(a [double]
), even though the result would fit into a [long]
integer.([int]::maxvalue + [long] 1).GetType().FullName
yields System.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
and 1.0 / 1
and 1 / [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
yields 2147483648
, but note that this technique only works up to 0x7fffffffffffffff
, 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
, or ul
, 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
and 1e0
both yield a [double]
Your problem is that the ToUint64
doesn't understand the Powershell syntax. You could get around it by doing:
($MemoryFromString / 1GB) * 1GB
As the $MemoryFromString
will be converted its numeric value before the division.
This works because at the point of division Powershell attempts to convert the string to a number using its rules, rather than the .Net rules that are baked into ToUInt64
. As part of the conversion if spots the GB
suffix and applies it rules to expand the "1GB"
string to 1073741824
EDIT: Or as PetSerAl pointed out, you can just do:
($MemoryFromString / 1)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With