The disagreement is about which address bits define the network.
An IPv4 address contains four decimal octets, each from 0 to 255. The suffix /20 means the first 20 of its 32 bits identify the prefix. It does not mean “the first 20 decimal
digits” or “20 addresses.” The subnet mask writes those first 20 positions as ones and the remainder
as zeroes.
- Host
192.168.34.77- Prefix
/20means 20 network bits in a 32-bit IPv4 address.- Competing claim
- The configured network is
192.168.32.0/20. - Question
- Can the host address be reduced to that network address by applying the mask?
Each position contributes a power of two.
Binary is base two. Moving one place left doubles the place value: … 16, 8, 4, 2, 1. The bit 00100010₂ has a one in the 32 place and the 2 place, so its value
is 32 + 2 = 34. A bit position is a yes/no contribution to the total, not a
decimal digit with value zero through nine.
For a four-bit group, the values are 8, 4, 2, 1. This is why a hexadecimal
digit can stand for exactly four bits: 0010₂ = 2₁₆, and 1110₂ = E₁₆. Binary makes individual decisions visible; decimal is usually
easier for people to read quantities; hexadecimal is a compact way to preserve bit
grouping.
| Value | Binary | Meaning |
|---|---|---|
| Host 34 | 00100010 | 32 + 2 |
| Mask 240 | 11110000 | Keep the upper four bits of this octet |
| AND result 32 | 00100000 | 32 + 0 |
192.168.34.77 & 255.255.240.0 = 192.168.32.0.Each operator answers a different bit-level question.
- AND (
&) keeps a bit only when both inputs have 1. Use it to extract fields or apply a network mask. - OR (
|) sets a bit when either input has 1. Useflags | READto turn a permission flag on without changing other bits. - XOR (
^) sets a bit when inputs differ. XOR with one flag toggles that flag; XOR with a comparison value can identify changed bits. - Shift left/right (
<<,>>) moves bit positions. For small nonnegative integers, shifting left by one doubles the value; shifts also help position a field or construct a mask.
A flag should be tested with (value & FLAG) !== 0, not equality with the
flag: the value can contain other flags at the same time. Clear a one-bit flag using value & ~FLAG, while preserving the rest.
0010 0101₂ · flags at bits 5, 2, and 0
0000 0100₂ · test bit 2
0010 0101 & 0000 0100 = 0000 0100 · set
0x25 & ~0x04 clears bit 2, retaining bits 5 and 0
Hexadecimal shortens the writing without changing the bits.
Hexadecimal is base 16, so each digit represents one four-bit nibble. For example, 0xF0 is 1111 0000₂; 0x04 is 0000 0100₂. The 0x prefix is a convention that labels the number as hexadecimal. It does not indicate
a different kind of storage.
The `/20` mask is 255.255.240.0 in dotted decimal and 0xFFFFF000 as a 32-bit word. Four consecutive one bits become one F. Hex is useful in
packet dumps and flag fields because changes align visually with groups of four bits; when
a particular bit is unclear, expand it to binary.
0xFFFFF000 = 11111111 11111111 11110000 00000000₂192.168.34.77 = 11000000 10101000 00100010 01001101₂AND = 11000000 10101000 00100000 00000000₂ = 192.168.32.0Represent the same 32 bits explicitly in each language.
The TypeScript version parses the four octets into network byte order with DataView, applies a prefix mask, then converts the result back to unsigned with >>> 0. For 192.168.34.77/20, both programs print 192.168.32.0. The Go version uses net/netip, which parses and
masks an IPv4 prefix without relying on architecture-sized int arithmetic.
JavaScript’s ordinary Number bitwise operators convert operands to signed 32-bit integers.
IPv4 values with the high bit set (addresses from 128.0.0.0 onward) can
therefore appear negative as intermediates. >>> 0 reinterprets the
32 result bits as an unsigned number; DataView makes byte order explicit. The
sample validates its input and handles /0 separately because JavaScript shift counts
wrap modulo 32: shifting by 32 behaves like shifting by zero.
Both versions validate the IPv4 prefix and produce the same network address.
/** Read an IPv4 address as an unsigned, network-byte-order 32-bit value. */
export function ipv4ToUint32(address: string): number {
const parts = address.split('.');
const octets = parts.map(Number);
if (
octets.length !== 4 ||
octets.some((octet, index) => !/^\d+$/.test(parts[index] ?? '') || octet < 0 || octet > 255)
) {
throw new RangeError('address must contain four decimal octets from 0 to 255');
}
const bytes = new Uint8Array(octets);
return new DataView(bytes.buffer).getUint32(0, false);
}
/** Return the canonical network address for an IPv4 address and prefix. */
export function networkAddress(address: string, prefixLength: number): string {
if (!Number.isInteger(prefixLength) || prefixLength < 0 || prefixLength > 32) {
throw new RangeError('prefix length must be an integer from 0 to 32');
}
const ip = ipv4ToUint32(address);
// Avoid JavaScript's shift-count modulo 32 behavior for the /0 boundary.
const mask = prefixLength === 0 ? 0 : (0xffff_ffff << (32 - prefixLength)) >>> 0;
// Number bitwise operators coerce to signed int32; >>> 0 restores the uint32 value.
const network = (ip & mask) >>> 0;
const bytes = new ArrayBuffer(4);
new DataView(bytes).setUint32(0, network, false);
return Array.from(new Uint8Array(bytes)).join('.');
}
function assertUint32(value: number): void {
if (!Number.isInteger(value) || value < 0 || value > 0xffff_ffff) {
throw new RangeError('flag values must be unsigned 32-bit integers');
}
}
function hasFlag(value: number, flag: number): boolean {
assertUint32(value);
assertUint32(flag);
return (value & flag) !== 0;
}
function setFlag(value: number, flag: number): number {
assertUint32(value);
assertUint32(flag);
return (value | flag) >>> 0;
}
function clearFlag(value: number, flag: number): number {
assertUint32(value);
assertUint32(flag);
return (value & ~flag) >>> 0;
}
function toggleFlag(value: number, flag: number): number {
assertUint32(value);
assertUint32(flag);
return (value ^ flag) >>> 0;
}
const address = '192.168.34.77';
console.log(`${address}/20 → ${networkAddress(address, 20)}`);
console.log(`0x25 has 0x04: ${hasFlag(0x25, 0x04)}`);
console.log(`set 0x08: 0x${setFlag(0x25, 0x08).toString(16)}`);
console.log(`clear 0x04: 0x${clearFlag(0x25, 0x04).toString(16)}`);
console.log(`toggle 0x04: 0x${toggleFlag(0x25, 0x04).toString(16)}`);
package main
import (
"fmt"
"net/netip"
)
func networkAddress(address string, prefixLength int) (string, error) {
ip, err := netip.ParseAddr(address)
if err != nil || !ip.Is4() {
return "", fmt.Errorf("address must be a valid IPv4 address")
}
if prefixLength < 0 || prefixLength > 32 {
return "", fmt.Errorf("prefix length must be from 0 to 32")
}
prefix := netip.PrefixFrom(ip, prefixLength).Masked()
return prefix.Addr().String(), nil
}
func hasFlag(value, flag uint32) bool {
return value&flag != 0
}
func setFlag(value, flag uint32) uint32 {
return value | flag
}
func clearFlag(value, flag uint32) uint32 {
return value &^ flag
}
func toggleFlag(value, flag uint32) uint32 {
return value ^ flag
}
func main() {
address := "192.168.34.77"
network, err := networkAddress(address, 20)
if err != nil {
panic(err)
}
fmt.Printf("%s/20 → %s\n", address, network)
flags := uint32(0x25)
fmt.Printf("0x25 has 0x04: %t\n", hasFlag(flags, 0x04))
fmt.Printf("set 0x08: 0x%02x\n", setFlag(flags, 0x08))
fmt.Printf("clear 0x04: 0x%02x\n", clearFlag(flags, 0x04))
fmt.Printf("toggle 0x04: 0x%02x\n", toggleFlag(flags, 0x04))
}
- MDN: JavaScript expressions and operators explains bitwise Number conversion to 32-bit values.
- MDN: unsigned right shift explains zero-fill behavior and the 32-bit shift-count boundary.
- Go specification: arithmetic operators defines integer bitwise and shift operators.
- Go net/netip Prefix.Masked documents canonical prefix masking.
Language references checked 2026-10-01. They specify operator behavior; they do not replace a protocol or network configuration contract.
The same bit reasoning appears in flags, permissions, and protocol fields.
- Field
0x25(binary0010 0101)- Flag definition
- Checksum-present is bit 2, represented by
0x04(0000 0100). - Task
- Use AND to decide whether the flag is present; then compute the value after clearing it.
Show a worked answer
0x25 & 0x04 = 0x04, so the flag is set. Clear it with 0x25 & ~0x04, giving 0x21 in a fixed 8-bit flag field. In languages
with wider or signed representations, reason about the field width and mask explicitly; an
unbounded bitwise complement flips every representable bit, even when the protocol defines
only eight.