Number systems and binary arithmetic¶
About this chapter Boolean and numerical logic
In this chapter
- Number systems and binary arithmetic
- Prerequisites and scope
- Positional notation
- Binary place values
- Powers of two and capacity
- Binary, hexadecimal and octal grouping
- Decimal-to-base conversion
- Horner evaluation
- Parsing overflow
- Fixed-width arithmetic as modular arithmetic
- Carry
- Subtraction and borrow
- Two's-complement signed interpretation
- Negation in two's complement
- Signed overflow
- Carry and overflow are independent
- Sign extension
- Zero extension
- Truncation
- Bitwise operations versus arithmetic
- Masks and fields
- Left shift
- Logical right shift
- Arithmetic right shift
- Rotations
- Fractional positional notation
- Fixed-point representation
- Floating-point boundary
- Endianness and significance are distinct
- ChrisArchitectureState and numerical width
- ChrisCPU operand masks
- ChrisCPU widened addition
- ChrisCPU signed-overflow predicates
- Conditions and numerical interpretation
- ChrisASM numeric parsing
- ChrisASM little-endian emission
- Graphics packing as positional arithmetic
- Initialization and control-flow boundary
- State and data structures
- Algorithms and complexity
- Memory ownership
- ABI and format boundary
- Concurrency
- Failure modes
- Security implications
- Performance considerations
- Current Intel architectural context
- Validation evidence for this chapter
- Current limitations
- Roadmap boundary
- Revision provenance
Prerequisites and scope¶
The required prerequisite is Boolean algebra. Individual bits already have values 0 or 1 and Boolean operations have defined truth tables. This chapter adds numerical interpretation.
Boolean bits
↓
ordered bit vector
↓
positional numerical interpretation
↓
finite-width arithmetic
↓
machine instructions and data formats
The same bit vector can represent an unsigned integer, a signed integer, a mask, an address, a color, an instruction field or opaque data. Arithmetic is valid only after interpretation and width are known.
Positional notation¶
A base-b positional numeral with digits d_n ... d_1 d_0 represents:
where every digit satisfies 0 <= d_i < b.
For decimal:
For binary:
For hexadecimal:
The numeral is notation. The mathematical integer is independent of the notation used to write it.
Binary place values¶
For an n-bit vector b_(n-1)...b_0, the unsigned value is:
Bit i has weight 2^i.
| Bit | Weight |
|---|---|
| 7 | 128 |
| 6 | 64 |
| 5 | 32 |
| 4 | 16 |
| 3 | 8 |
| 2 | 4 |
| 1 | 2 |
| 0 | 1 |
The least significant bit has the smallest weight. The most significant bit has the largest positional weight in the fixed-width word.
Powers of two and capacity¶
An n-bit vector has 2^n distinct patterns.
For unsigned interpretation:
| Width | Patterns | Unsigned range |
|---|---|---|
| 1 | 2 | 0..1 |
| 4 | 16 | 0..15 |
| 8 | 256 | 0..255 |
| 16 | 65,536 | 0..65,535 |
| 32 | 2^32 | 0..4,294,967,295 |
| 64 | 2^64 | 0..18,446,744,073,709,551,615 |
Width is part of the contract. The pattern 11111111 is 255 only under an 8-bit unsigned interpretation.
Binary, hexadecimal and octal grouping¶
One hexadecimal digit corresponds to exactly four bits:
One octal digit corresponds to three bits:
Hexadecimal aligns naturally with byte-oriented words because one byte is two hexadecimal digits.
Decimal-to-base conversion¶
To convert a nonnegative integer to base b, repeatedly divide by b and collect remainders.
For 45 to binary:
45 / 2 = 22 remainder 1
22 / 2 = 11 remainder 0
11 / 2 = 5 remainder 1
5 / 2 = 2 remainder 1
2 / 2 = 1 remainder 0
1 / 2 = 0 remainder 1
Reading remainders upward gives 101101₂.
The algorithm requires O(log_b N) divisions for positive N.
Horner evaluation¶
A numeral can be parsed without explicitly evaluating powers:
For k digits this requires O(k) arithmetic steps and O(1) auxiliary state.
Current ChrisASM parse_u64 follows this form for base 10 or 16 and checks for overflow before each multiply-add.
Parsing overflow¶
With current value v, next digit d and maximum M, the new value would be:
A safe precondition is:
ChrisASM uses this pattern with M = 18446744073709551615, which equals 2^64 - 1.
That is the parser's concrete contract, not a universal integer-parsing API.
Fixed-width arithmetic as modular arithmetic¶
For an n-bit unsigned word:
Example in eight bits:
The stored byte is 00000100.
The full mathematical result and the stored finite-width result are different objects.
Carry¶
For unsigned addition:
with 0 <= r < 2^n.
The stored result is r and q represents carry-out for two n-bit addends without another carry-in.
For 250 + 10:
so the stored result is 4 and carry is 1.
Subtraction and borrow¶
Finite-width subtraction also wraps modulo 2^n:
Thus, in eight bits:
For unsigned interpretation, ordinary subtraction needs a borrow when a < b. Carry/borrow flag polarity is an ISA contract.
Two's-complement signed interpretation¶
For an n-bit pattern with unsigned value U:
The signed range is:
For eight bits:
The bits do not change between signed and unsigned interpretations. The mapping changes.
Negation in two's complement¶
For an n-bit word x:
Example:
The minimum signed value has no positive counterpart at the same width. In eight bits, negating -128 cannot produce +128 as a representable signed 8-bit value.
Signed overflow¶
Signed overflow differs from unsigned carry.
For addition, signed overflow occurs when both operands have the same sign and the stored result has the opposite sign.
Example:
Unsigned 127 + 1 = 128 fits, so there is no unsigned carry. Signed 127 + 1 exceeds the signed 8-bit maximum, so signed overflow occurs.
Carry and overflow are independent¶
| 8-bit operation | Result | Carry | Signed overflow |
|---|---|---|---|
| 1 + 1 | 0x02 | 0 | 0 |
| 0xFF + 1 | 0x00 | 1 | 0 |
| 0x7F + 1 | 0x80 | 0 | 1 |
| 0x80 + 0x80 | 0x00 | 1 | 1 |
No single status bit answers both the unsigned and signed range questions.
Sign extension¶
Sign extension preserves a signed value while increasing width.
If the source sign bit is zero, new upper bits are zero. If the source sign bit is one, new upper bits are one.
Zero extension¶
Zero extension preserves an unsigned value:
Zero-extending a negative signed pattern changes its signed numerical interpretation.
Truncation¶
Reducing a bit-vector width keeps the low-order bits in ordinary modular reasoning:
Example:
Truncation is safe only when loss of upper information is impossible or intended. Accidental narrowing of addresses, lengths or sizes is a correctness and security risk.
Bitwise operations versus arithmetic¶
Bitwise operations act independently on bit positions:
Arithmetic operations interpret the vector as a numerical word and propagate carries or borrows across positions.
For example:
XOR is one-bit addition without carry, but multi-bit integer addition is not equivalent to XOR.
Masks and fields¶
A mask selects or modifies bit fields.
To test bit k:
To set bit k:
To clear bit k:
To extract a field:
The operation is mechanical; semantic meaning comes from the field-layout contract.
Left shift¶
For an unsigned n-bit word, left shift by k corresponds to multiplication by 2^k modulo the width when shifted-out bits are discarded:
Bits leaving the word are lost.
A language or ISA can impose additional rules for shift counts; those rules must be read from the actual execution contract.
Logical right shift¶
Logical right shift inserts zeros at the high end.
For unsigned values:
under the ordinary fixed-width interpretation.
Arithmetic right shift¶
Arithmetic right shift of a negative two's-complement value replicates the sign bit under the usual x86 behavior.
Logical and arithmetic right shifts are therefore different when the top bit is one.
Rotations¶
A rotate moves shifted-out bits back into the opposite side.
Rotation preserves bit population but is not ordinary multiplication or division.
Fractional positional notation¶
Binary positional notation extends below the radix point.
Not every decimal fraction has a finite binary representation. Decimal 0.1 repeats in binary, which is one source of representation error in floating-point computation.
Fixed-point representation¶
A fixed-point word assigns an implicit scale.
If integer pattern I represents:
then F bits act as fractional precision.
Fixed-point arithmetic can reuse integer hardware, but the contract must define:
- scale;
- signedness;
- rounding;
- multiplication width;
- overflow behavior.
The current reviewed ChrisOS tree does not define one universal fixed-point ABI.
Floating-point boundary¶
Floating-point represents sign, exponent and significand rather than a single fixed binary-point location.
It requires separate treatment of:
- normalized and subnormal numbers;
- infinities;
- NaNs;
- rounding;
- exceptions.
ChrisArchitectureState contains XMM byte storage, but that alone does not establish complete floating-point execution semantics in ChrisCPU.
Endianness and significance are distinct¶
The 32-bit integer:
has fixed numerical bit weights.
In little-endian memory its bytes appear at increasing addresses as:
The numerical value remains 0x12345678 when loaded under the corresponding byte-order contract.
Endianness changes byte storage order; it does not redefine positional significance inside the abstract integer.
ChrisArchitectureState and numerical width¶
Current ChrisOS main defines many architectural fields as uint64_t in ChrisArchitectureState.
The general-purpose register array is:
and named fields such as rax, rcx and rip use 64-bit storage.
This does not mean every x86 operation is 64-bit. Instruction semantics select operand width, while the container remains wide enough to store architectural state.
Container width and active operand width are distinct contracts.
ChrisCPU operand masks¶
The reviewed flags.c defines masks conceptually as:
chris_flags_bin masks both operands before arithmetic.
For its expected operand-size values, the helper therefore models:
bit arithmetic.
It is not an arbitrary-width integer library.
ChrisCPU widened addition¶
For ADD and ADC, ChrisCPU uses an unsigned 128-bit host intermediate:
It retains the low architectural-width bits and detects carry from the bits above the selected width.
This is a software technique for preserving the mathematical intermediate result long enough to derive a narrower architectural result and carry.
It does not mean an ordinary guest ADD architecturally produces a 128-bit destination.
ChrisCPU signed-overflow predicates¶
For addition, current source computes a predicate equivalent to:
For subtraction it uses:
These are Boolean expressions over finite-width two's-complement patterns.
They separate signed overflow from unsigned carry or borrow.
Conditions and numerical interpretation¶
chris_cc_true uses different status combinations for unsigned and signed order.
Conceptually:
- CF participates in unsigned comparisons;
- SF and OF together participate in signed comparisons;
- ZF represents equality.
The same bits in a register can therefore be ordered differently depending on interpretation.
Signedness is not permanently attached to the stored pattern.
ChrisASM numeric parsing¶
The reviewed parse_u64 accepts decimal text by default and hexadecimal text with 0x or 0X prefix.
It does not currently accept a 0b binary prefix in that function.
Each digit is accumulated by:
after overflow checking.
This is a direct implementation of positional-numeral evaluation.
ChrisASM little-endian emission¶
emit_u32 decomposes a value as:
emit_u64 applies the same principle to eight bytes.
Thus:
A numerical value is first established, then serialized according to a byte-order contract.
Graphics packing as positional arithmetic¶
gfx_rgb packs three byte-sized channels:
The fields occupy:
Because they do not overlap, the same packed value can be expressed numerically as:
This is positional arithmetic applied to a software data layout.
Initialization and control-flow boundary¶
Number systems have no runtime initialization.
The relevant software control flows are ordinary consumers of numerical contracts:
and:
decoded operands
↓
chris_flags_bin
↓
width-masked result + status flags
↓
instruction execution state
The mathematics is stateless; the software paths using it are not.
State and data structures¶
Relevant reviewed structures include:
- ChrisArchitectureState for architectural register/state storage;
- assembler static byte buffers and length counters;
- caller-owned result storage passed to chris_flags_bin;
- packed uint32_t color values produced by gfx_rgb.
The underlying integer interpretation remains a contract over bits.
No separate runtime object called a number system exists.
Algorithms and complexity¶
The main algorithms in this chapter have explicit costs:
| Operation | Complexity |
|---|---|
| parse k digits with Horner evaluation | O(k) |
| format positive integer N in base b | O(log_b N) digit extractions |
| fixed-width mask/shift on one machine word | O(1) at this abstraction |
| sign/zero extension of one native-width word | O(1) |
| arbitrary-precision arithmetic | outside this chapter |
ChrisASM parse_u64 uses O(k) time and O(1) auxiliary state for a bounded token.
Memory ownership¶
Numerical values do not own memory; storage does.
In the reviewed code:
- ChrisArchitectureState is part of emulator CPU state;
- chris_flags_bin receives values and optionally writes through a caller-owned result pointer;
- parse_u64 writes to a caller-provided uint64_t;
- assembler emitters mutate assembler-owned static section buffers;
- gfx_rgb returns a value and allocates nothing.
The arithmetic rule and the ownership rule are separate.
ABI and format boundary¶
Any stable ABI or persistent format must specify enough information to reconstruct the integer meaning:
- bit width;
- signedness;
- byte order;
- field position;
- scaling if fixed-point;
- reserved values;
- overflow or wrap expectations where relevant.
The phrase "integer field" is insufficient for a durable binary contract.
The data-representation chapter applies these rules to pointers, structures, object formats, filesystems and device layouts.
Concurrency¶
Pure arithmetic on local values has no shared-state race.
A shared-memory update is a different problem.
is not automatically atomic because integer addition is mathematically well-defined.
The implementation performs a read, computes a value and writes a value unless an architectural atomic primitive or synchronization mechanism provides a stronger contract.
Therefore:
Failure modes¶
| Error | Consequence |
|---|---|
| wrong width | truncation or wrong mask |
| wrong signedness | incorrect comparison/range |
| unchecked unsigned wrap | size/address error |
| incorrect sign extension | wrong negative value |
| incorrect zero extension | changed signed interpretation |
| bad shift count | language/ISA-specific fault or wrong result |
| wrong byte order | corrupted encoded value |
| parser overflow | invalid constant accepted or rejected incorrectly |
| mixed fixed-point scales | numerically plausible but wrong result |
The stored bits can look valid even when their interpretation is wrong.
Security implications¶
Integer mistakes are a core systems-security boundary.
Common dangerous patterns include:
- allocation-size multiplication overflow;
- bounds-check truncation;
- signed/unsigned comparison mismatch;
- pointer narrowing;
- shift-derived mask errors;
- length addition wraparound.
Unsigned modular behavior can be deterministic and still be semantically unsafe.
Code controlling memory sizes or addresses should prove the range before relying on a finite-width result.
Performance considerations¶
For native fixed-width integers, individual arithmetic operations are treated as constant-time at the algorithmic level used here, although actual instruction latency and throughput depend on the microarchitecture.
Base conversion is not constant in input length.
A k-digit parse is O(k), while formatting a positive integer N in base b takes O(log_b N) digit extraction steps.
Bit tricks should not replace clearer arithmetic merely because they appear lower level. Compiler output and measured performance are the relevant evidence for optimization.
Current Intel architectural context¶
Intel's public Intel 64 and IA-32 Software Developer's Manual set was updated in September 2026 and the manual page lists version 093.
Volume 1 describes the basic architecture and programming environment; Volume 2 defines instruction semantics.
These manuals establish the architectural contracts for register widths, arithmetic, shifts and status flags on Intel 64/IA-32 processors.
ChrisCPU must be validated against the particular architectural semantics it intends to emulate rather than against generic intuition about binary arithmetic.
Validation evidence for this chapter¶
The chapter-specific deterministic checker validates:
101101₂ = 45
0x2D = 45
max unsigned n-bit value = 2^n - 1
(250 + 10) mod 256 = 4
signed_8(0xFF) = -1
signed_8(0x80) = -128
sign_extend_8_to_16(0xFB) = 0xFFFB
zero_extend_8_to_16(0xFB) = 0x00FB
unsigned left-shift modulo width
Horner parsing
RGB packing:
red·2^16 + green·2^8 + blue
The checker also verifies current source anchors in flags.c, chris_arch.h, chrisasm.c and graphics.c.
It does not claim complete x86 arithmetic conformance.
Current limitations¶
This chapter intentionally does not attempt to fully cover:
- arbitrary-precision integer algorithms;
- IEEE 754 floating-point semantics;
- decimal floating-point;
- cryptographic multiprecision arithmetic;
- SIMD lane arithmetic;
- saturating arithmetic;
- the complete C integer-conversion model;
- every x86 arithmetic instruction.
Those belong to later or specialized treatments.
Roadmap boundary¶
The curriculum transition is:
logic levels
↓
Boolean algebra
↓
number systems and finite-width arithmetic
↓
data representation and layout
↓
combinational arithmetic circuits
↓
ISA-visible registers and machine code
The existing data-representation chapter depends on this chapter because width, signedness, masking and byte order require an explicit finite-bit numerical model.
Revision provenance¶
Implementation-facing statements were reconciled against ChrisOS main revision da3df29cb397932c43d32373871fb9380e688ade.
Reviewed sources:
- chrisvm/cpu/emulator/flags.c;
- chrisvm/chris_arch.h;
- compiler/chrisasm/chrisasm.c;
- kernel/gfx/graphics.c.
Reviewed symbols:
- chris_flags_bin;
- chris_cc_true;
- ChrisArchitectureState;
- parse_u64;
- emit_u32;
- emit_u64;
- gfx_rgb.
The current Intel 64 and IA-32 Software Developer's Manual page was checked as the primary architectural reference; Intel lists the manual set as version 093 in September 2026. WG14 committee material was checked for contemporary C23 integer-representation context, but this chapter does not substitute committee discussion for a complete language-standard treatment.