SheepDev

Numbers, units, and bits

Why can ff mean either 255 or -1?

Tools to use

One log shows 255 and another −1 for what seems to be the same byte. Before blaming corruption, check signedness. FF describes eight set bits but does not by itself choose their integer interpretation. Fixing width and signedness in Bitwise Calculator separates changed data from a different reading of the same pattern.

Start with unsigned eight-bit interpretation

Three notations for one pattern
11111111 (binary)
ff (hexadecimal)
255 (unsigned decimal)

Choose width 8, unsigned, base 16 and OR; enter FF and 0. OR with zero preserves the pattern, yielding decimal 255, binary 11111111 and hex ff. With base 10, FF is not valid input. The input base is part of the interpretation too.

Unsigned eight-bit integers range from 0 to 255; 256 needs nine bits. Check width 8 in Integer Range to explain a rejected input. Understanding 255 as 2⁸−1 scales better than memorizing it as a special value.

Enter −1 and compare its bits

Eight-bit two's complement
255 − 256 = −1
11111111 → −1 (signed)
11111111 → 255 (unsigned)

Now choose signed, decimal input and enter −1 OR 0 at width 8. Decimal is −1 but the unsigned value is 255 and bits remain 11111111. In two's complement, a set high bit means subtracting 2ʷ from the unsigned value. This is 255−256, not a sign bit attached to magnitude 127.

Signed width 8 accepts −128 through 127. FF as a hexadecimal numeric input is 255, so the default reject policy refuses it in signed mode. Truncation can explicitly reinterpret the retained bits, but that is a separate operation, not an automatic consequence of switching signedness.

FF and FFFF use different-sized fields

When the field is sixteen bits
0000000011111111 = 255
1111111111111111 = −1 (signed 16-bit)
NOT 0, unsigned 8-bit  → 255
NOT 0, unsigned 16-bit → 65535

Signed sixteen-bit 00FF is 255 because its top bit is zero; −1 is FFFF. Preserving a negative value across wider signed representations requires sign extension, not zero padding. A protocol's one-byte versus two-byte field affects meaning, not just display width.

NOT 0 yields 255 at width 8 and 65535 at width 16. This calculator masks results to the selected width. When comparing with a language's operators, such as Number bitwise operations in JavaScript, match its width conventions too. Arbitrary-size integers do not eliminate fixed-width rules.

For negative right shifts, inspect the fill bits

Shift eight-bit −2 right by one
11111110 → 01111111 (logical)   = 127
11111110 → 11111111 (arithmetic) = −1

At signed width 8, shift −2 right by one. Logical shift fills with zero and yields 127; arithmetic shift preserves the sign and yields −1. Saying merely 'right shift halves the number' misses this distinction. Choose based on whether you are manipulating a bit pattern or signed arithmetic.

Do not use truncation to hide an invalid value

Truncating 256 to eight bits leaves zero. That is information loss, not successful preservation. Reject mode prevents it. Choose truncation only when the format explicitly requires wrapping or retaining lower bits, not by changing settings until a convenient result appears.

Record the original FF, base 16, width 8 and signedness. For multibyte data also verify byte order from its specification. Matching integer interpretation does not prove file byte order or field boundaries. Once all conditions match, a remaining discrepancy is a reason to compare the actual extracted data.

Sources

Tools to use

Guides →