Last active
July 14, 2025 20:16
-
-
Save Bill-Stewart/e97e02d761f41a8a04a831135774a604 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#requires -version 3 | |
# Version history: | |
# | |
# 2025-07-14 | |
# * Added spaces in binary string output to improve readability. | |
# | |
# 2025-07-11 | |
# * Initial version. Replaces an older script. | |
<# | |
.SYNOPSIS | |
Outputs the bits that are set in a numeric value. | |
.DESCRIPTION | |
Outputs the positions of individual bits that are set in a numeric value. The value must fit in the range -2147483648 (i.e., the smallest possible signed 32-bit value) through 18446744073709551615 (i.e., the largest possible unsigned 64-bit value). A negative value is cast to a 32-bit unsigned integer. | |
.PARAMETER Value | |
Specifies the value. | |
.OUTPUTS | |
The output contains the following information: | |
* The value in decimal (including the signed decimal value if 32 bit) | |
* The value in hexadecimal | |
* The value in binary | |
* A list of the bits set in the value | |
Hexadecimal values are output in byte pairs. | |
.EXAMPLE | |
PS C:\> Get-BitFlags -2147483642 | |
Dec: 2147483654 (signed: -2147483642) | |
Hex: 8000 0006 | |
Bin: 10000000 00000000 00000000 00000110 | |
Bit Hex Dec | |
--- --- --- | |
2^31 8000 0000 2147483648 | |
2^2 0000 0004 4 | |
2^1 0000 0002 2 | |
The output shows that the negative value -2147483642 is 2147483654 as an unsigned 32-bit decimal value, or 80000006 in hexadecimal. In this value, bits are set in the following positions: 2^31, 2^1, and 2^1. | |
#> | |
[CmdletBinding()] | |
param( | |
[Parameter(Position = 0,Mandatory)] | |
[String] | |
$Value | |
) | |
# Outputs a string as a 64-bit unsigned integer; a negative value will be cast | |
# to a 32-bit unsigned integer | |
function ConvertTo-Unsigned { | |
param( | |
[String] | |
$value | |
) | |
if ( $value.IndexOf(".") -eq -1 ) { | |
$value = $value.Trim() | |
if ( $value[0] -eq "-" ) { | |
$int32 = $value -as [Int32] | |
if ( $null -ne $int32 ) { | |
[BitConverter]::ToUInt32([BitConverter]::GetBytes($int32),0) | |
} | |
} | |
else { | |
$value -as [UInt64] | |
} | |
} | |
} | |
# Outputs a 32-bit unsigned integer as a signed integer | |
function ConvertTo-Signed { | |
param( | |
[UInt32] | |
$value, | |
[Int] | |
$bits | |
) | |
[BitConverter]::ToInt32([BitConverter]::GetBytes($value),0) | |
} | |
# Tests whether an unsigned integer can be interpreted as 32-bit signed | |
function Test-Signed { | |
[CmdletBinding()] | |
param( | |
[Parameter(Position = 0,Mandatory)] | |
[UInt64] | |
$value | |
) | |
if ( $value -gt [UInt32]::MaxValue ) { | |
return $false | |
} | |
(ConvertTo-Signed $value) -lt 0 | |
} | |
# Outputs the bit width for an unsigned integer "rounded up": | |
# * 0x100000000 or greater = 64 bits | |
# * 0x1000 to 0xFFFFFFFF = 32 bits | |
# * 0x100 to 0xFFFF = 16 bits | |
# * 0 to 0xFF = 8 bits | |
function Get-BitWidth { | |
[CmdletBinding()] | |
param( | |
[Parameter(Position = 0,Mandatory)] | |
[UInt64] | |
$value | |
) | |
if ( $value -gt [UInt32]::MaxValue ) { return 64 } | |
elseif ( $value -gt [UInt16]::MaxValue ) { return 32 } | |
elseif ( $value -gt [Byte]::MaxValue ) { return 16 } | |
else { return 8 } | |
} | |
# Outputs unsigned integer as hex string byte pairs using the specified width; | |
# if width not specified, output using automatic width | |
function ConvertTo-HexString { | |
param( | |
[UInt64] | |
$value, | |
[Int] | |
[ValidateSet(0,2,4,8,16)] | |
$width = 0 | |
) | |
if ( $width -eq 0 ) { | |
$width = (Get-BitWidth $value) / 4 | |
} | |
("{0:X$width}" -f $value) -replace '....(?!$)','$0 ' | |
} | |
# Outputs unsigned integer as binary string with leading zeros with a space | |
# between each byte | |
function ConvertTo-BinaryString { | |
param( | |
[Parameter(Position = 0,Mandatory)] | |
[UInt64] | |
$value | |
) | |
# Why character array? | |
# * "b" string format specifier not universally available | |
# * [Convert]::ToString doesn't support UInt64 values | |
$width = Get-BitWidth $value | |
$chars = New-Object Char[] $width | |
for ( $i = $width - 1; $i -ge 0; $i-- ) { | |
$chars[$i] = ('0','1')[$value -band 1] | |
$value = $value -shr 1 | |
} | |
($chars -join '') -replace '........(?!$)','$0 ' | |
} | |
$uInt = ConvertTo-Unsigned $Value | |
if ( $null -eq $uInt ) { | |
Write-Error "Unable to convert '$Value' to an unsigned integer." -Category InvalidArgument | |
return | |
} | |
# Build and output header to host | |
$header = "Dec: $uInt" | |
if ( Test-Signed $uInt ) { | |
$header += " (signed: {0})" -f (ConvertTo-Signed $uInt) | |
} | |
$header += "`nHex: {0}" -f (ConvertTo-HexString $uInt) | |
$header += "`nBin: {0}" -f (ConvertTo-BinaryString $uInt) | |
Write-Host $header | |
# Get width of hex strings for output objects | |
$hexWidth = (Get-BitWidth $uInt) / 4 | |
# Output each set bit starting at most significant bit (2^63) | |
$bit = 63 | |
for ( [UInt64] $i = [UInt64] [Math]::Pow(2,$bit); $i -gt 0; $i /= 2 ) { | |
if ( ($uInt -band $i) -ne 0 ) { | |
[PSCustomObject] @{ | |
"Bit" = "2^{0}" -f $bit | |
"Hex" = ConvertTo-HexString $i $hexWidth | |
"Dec" = $i | |
} | |
} | |
$bit-- | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment