1913 words
10 minutes
The CSAPP Notebook
2025-07-16
2025-07-21

前言#

不开心,不想说话……让我们直接进入这段有趣的学习之旅吧,试试两个月,甚至更短的时间内解决掉这门又臭又长的课程。

Bits, Bytes, and Integers#

Representation information as bits#

Everything is bits#

Each bit is 0 or 1#

By encoding/interpreting sets of bits in various ways#

  • Computers determine what to do (instructions)
  • … and represent and manipulate numbers, sets, strings, etc…

Why bits? Electronic Implementation#

  • Easy to store with bitsable elements
  • Reliably transmitted on noisy and inaccurate wires
  • Easy to indicate low level/high level

Bit-level manipulations#

Representing & Manipulating Sets#

Representation#

We can use bits to determine whether an element belongs to a set. Say each bit have index, which corresponds to a number from zero. If a bit is set to 1, it means the corresponding index (i.e., the element) is in the set. Otherwise, its not.

In a more mathematical representation way:

  • Width ww bit vector represents subsets subsets of {0, ..., w1}\{0,\ ...,\ w-1\}
  • aj=1a_{j} =1 if jAj\in A

Sets Operations#

  • & is related to Intersection
  • | is related to Union
  • ~ is related to Complement
  • ^ is related to Symmetric difference

Shift Operations#

  • Left Shift: x << y

    • Shift bit-vector x left y positions
      • Throw away extra bits on left
    • Fill with 0’s on right
  • Right Shift: x >> y

    • Shift bit-vector x right y positions
      • Throw away extra bits on right
    • Logical shift
      • Fill with 0’s on left
    • Arithmetic shift
      • Replicate most significant bit on left

Undefined Behaviour#

  • Shift amount << 0
  • Shift amount \geqslant word size
  • Left shift a signed value

Integers#

Representation: unsigned and signed#

Unsigned#

B2U(X)=i=0w1xi2i\displaystyle B2U( X) =\sum _{i=0}^{w-1} x_{i} \cdot 2^{i}

Two’s Complement#

B2T(X)=xw12w1+i=0w2xi2i\displaystyle B2T( X) =-x_{w-1} \cdot 2^{w-1} +\sum _{i=0}^{w-2} x_{i} \cdot 2^{i}

Invert mappings#
  • U2B(x)=B2U1(x)U2B( x) =B2U^{-1}( x)
  • T2B(x)=B2T1(x)T2B( x) =B2T^{-1}( x)

Numeric Ranges#

  • Unsigned Values

    • UMin = 0\displaystyle UMin\ =\ 0
    • UMax = 2w1\displaystyle UMax\ =\ 2^{w} -1
  • Two’s Complement Values

    • TMin=2w1\displaystyle TMin = -2^{w-1}
    • TMax = 2w11\displaystyle TMax\ =\ 2^{w-1} -1
TIP

In C, these ranges are declared in limits.h. e.g., ULONG_MAX, LONG_MAX, LONG_MIN. Values are platform specific.

Observations#
  • TMin=TMax+1|TMin|=TMax+1
    • Asymmetric range (Every positive value can be represented as a negative value, but TMinTMin cannot be represented as a positive value)
  • UMax = 2TMax+1UMax\ =\ 2\cdot TMax+1

Difference between Unsigned & Signed Numeric Values#

The difference between Unsigned & Signed Numeric Values is 2w2^{w}.

For example, if you want convert a unsigned numeric value to its signed form, just use this value minus 2w2^{w}, or if you want convert a signed numeric value to its unsigned form, you plus 2w2^{w}.

Conversion, casting#

Mappings between unsigned and two’s complement numbers keep bit representations and reinterpret.

For example, casting a signed value to its unsigned form, the most significant bit from large negative weight becomes to large positive weight and vice versa.

Constants#

  • By default are considered to be signed integers.
  • Unsigned if have “U” as suffix

Casting#

  • Explicit casting between signed & unsigned same as U2TU2T and T2UT2U
  • Implicit casting also occurs via assignments and procedure call (assignments will casting to lhs’s type)

Expression Evaluation#

  • If there is a mix of unsigned and signed in single expression, signed values implicitly cast to unsigned
  • Including comparison operations <, >, ==, <=, >
Constants 1Constants 2RelationEvaluation
00U==unsigned
-10<signed
-10U>unsigned
2147483647-2147483647-1>signed
2147483647U-2147483647-1<unsigned
-1-2>signed
(unsigned)-1-2>unsigned
21474836472147483648U<unsigned
2147483647(int)2147483648U>signed

Expanding, truncating#

Sign Extension#

  • Make kk copies of sign bit
  • X=Xw1,...,Xw1,Xw1,Xw2,...,X0\displaystyle X\prime =X_{w-1} ,...,X_{w-1} ,X_{w-1} ,X_{w-2} ,...,X_{0}
WARNING

Converting from smaller to larger integer data type. C automatically performs sign extension.

Expanding (e.g., short int to int)#

  • Unsigned: zeros added
  • Signed: sign extension
  • Both yield expected result

Truncating (e.g., unsigned to unsigned short)#

  • Unsigned/signed: bits are truncated
  • Result reinterpreted
  • Unsigned: mod operation
  • Signed: similar to mod
  • For small numbers yields expected behavior

Addition, negation, multiplication, shifting#

Unsigned Addition#

  • Standard Addition Function
    • Ignore carry output
  • Implements Modular Arithmetic
    • UAddw(u,v)=(u+v)mod2w\displaystyle UAdd_{w}( u,v) =( u+v)\bmod 2^{w}
Visualizing (Mathematical) Integer Addition#
  • Integer Addition
    • 4-bit integers u,vu,v
    • Compute true sum Add4(u,v)Add_{4}( u,v)
    • Values increase linearly with uu and vv
    • Forms planar surface
Visualizing Unsigned Addition#
  • Wraps Around
    • If true sum 2w\geqslant 2^{w}
    • At most once

Two’s Complement Addition#

  • TAddTAdd and UAddUAdd have Identical Bit-level Behaviour

Signed vs. unsigned addition in C:

int s, t, u, v;
s = (int)((unsigned)u + (unsigned)v);
t = u + v;
// will give s == t
TAdd Overflow#
  • Functionality
    • True sum requires w+1w+1 bits
    • Drop off MSB
    • Treat remaining bits as two’s complement integer
Visualizing Two’s Complement Addition#
  • Values

    • 4-bit two’s comp.
    • Range from 8-8 to +7+7
  • Wraps Around

    • If sum 2w1\geqslant 2^{w-1}
      • Becomes negative
      • At most once
    • If sum <2w1< -2^{w-1}
      • Becomes positive
      • At most once

Multiplication#

  • Goal: Computing Product of ww-bit numbers x,yx,y
    • Either signed or unsigned
  • But, exact results can be bigger than ww bits
    • Unsigned: up to 2w2w bits
      • Result range: 0xy(2w1)2=22w2w+1+10\leqslant x\cdot y\leqslant \left( 2^{w} -1\right)^{2} =2^{2w} -2^{w+1} +1
    • Two’s complement min (negative): Up to 2w12w-1 bits
      • Result range: xy(2w1)(2w11)=22w2+2w1x\cdot y\geqslant \left( -2^{w-1}\right) \cdot \left( 2^{w-1} -1\right) =-2^{2w-2} +2^{w-1}
    • Two’s complement max (positive): Up to 2w2w bits, but only for (TMinw)2( TMin_{w})^{2}
      • Result range: xy(2w1)2=22w2x\cdot y\leqslant \left( -2^{w-1}\right)^{2} =2^{2w-2}
  • So, maintaining exact results…
    • would need to keep expanding word size with each product computed (exhaust memory faster)
    • is done in software, if needed
      • e.g., by “arbitrary precision” arithmetic packages
Unsigned Multiplication in C#
  • Standard Multiplication Function
    • Ignore high order ww bits
  • Implements Modular Arithmetic
    • UMultw(u,v)=(uv)mod2w\displaystyle UMult_{w}( u,v) =( u\cdot v)\bmod 2^{w}
Signed Multiplication in C#
  • Standard Multiplication Function
    • Ignore high order ww bits
    • Some of which are different for signed vs. unsigned multiplication
    • Lower bits are the same

Power-of-2 Multiply with Shift#

  • Operation
    • u << k gives u2ku\cdot 2^{k} (basically increases each bits weight)
    • Both signed and unsigned

Unsigned Power-of-2 Divide with Shift#

  • Quotient of Unsigned by Power of 2
    • u >> k gives u/2k\left\lfloor u/2^{k}\right\rfloor
    • Uses logical shift

Signed Power-of-2 Divide with Shift#

  • Quotient of Signed by Power of 2
    • Uses arithmetic shift
    • Want x/2k\lceil x/2^{k} \rceil (Round toward 0)
    • Compute as (x+2k1)/2k\left\lfloor \left( x+2^{k} -1\right) /2^{k}\right\rfloor
      • In C: (x + (1 << k) - 1) >> k
      • Biases dividend toward 0
Case 1: No rounding#
Case 2: Rounding#

Handy tricks#

If you want convert a value uu to its negative form by hand or in mind, either unsigned or signed.

Just do: u+1\sim u+1

When Should I Use Unsigned?#

Don’t use without understanding implications!

  • Do use when performing modular arithmetic
    • Multiplication arithmetic
  • Do use when using bits to represent sets
    • Logical right shift, no sign extension

Representation in memory, pointers, strings#

Byte-Oriented Memory Organization#

  • Programs refer to data by address
    • Conceptually, envision it as a very large array of bytes
      • In reality, it’s not, but can think of it that way
    • An address is like an index into that array
      • and, a pointer variable stores an address
  • System provides private address spaces to each “process”
    • So, a program can clobber its own data, but not that of others

Machine Words#

  • Any given computer has a “Word Size”
    • Nominal size of integer-valued data
      • and of addresses
    • Until recently, most machines used 32 bits (4 bytes) as word size
      • Limits addresses to 4GB (2322^{32} bytes)
    • Increasingly, machines have 64‐bit word size
      • Potentially, could have 18 EB (exabytes) of addressable memory
      • That’s 18.4×101818.4\times 10^{18} bytes
    • Machines still support multiple data formats
      • Fractions or multiples of word size
      • Always integral number of bytes

Word‐Oriented Memory Organization#

  • Addresses Specify Byte Locations
    • Address of first byte in word
    • Addresses of successive words differ by 4 (32‐bit) or 8 (64‐bit)

Byte Ordering#

  • Big Endian: Sun, PPC Mac, Internet
    • Least significant byte has highest address
  • Little Endian: x86, ARM processors running Android, iOS, and Windows
    • Least significant byte has lowest address

Representation Strings#

In C, either little endian or big endian machine, strings in memory represented in the same way, because a string is essentially an array of characters ends with \x00, each character is one byte encoded in ASCII format and single byte (character) do not obey the byte ordering rule.

Floating Point#

Background: Fractional binary numbers#

Representing#

  • Bits to right of “binary point” represent fractional powers of 2
  • Represents rational number: k=jibk2k\displaystyle \sum _{k=-j}^{i} b_{k} \cdot 2^{k}
ValueRepresentation
534\displaystyle5\frac{3}{4}101.112101.11_{2}
278\displaystyle2\frac{7}{8}10.111210.111_{2}
1716\displaystyle1\frac{7}{16}1.011121.0111_{2}

Observations#

  • Divide by 2 by shifting right (unsigned)
  • Multiply by 2 by shifting left
  • Numbers of form 0.11111120.111111\dots_{2} are just below 1.01.0
    • 12+14+18++12i+1.0\displaystyle \frac{1}{2} +\frac{1}{4} +\frac{1}{8} +\dots+\frac{1}{2^{i}} +\dots\rightarrow 1.0
    • Use notation 1.01.0ϵ\epsilon (ϵ\epsilon depends on how many bits you have to the right of the binary point. If it gets smaller the more, the more of those bits you have there, and it gets closer to 11)

Limitation 1#

  • Can only exactly represent numbers of the form x2k\displaystyle\frac{x}{2^{k}}
    • Other rational numbers have repeating bit representations, but cause computer system can only hold a finite number of bits, so 0.1+0.20.30.1+0.2\neq 0.3
ValueRepresentation
13\displaystyle\frac{1}{3}0.0101010101[01]20.0101010101[ 01] \dots_{2}
15\displaystyle\frac{1}{5}0.001100110011[0011]20.001100110011[ 0011] \dots_{2}
110\displaystyle\frac{1}{10}0.0001100110011[0011]20.0001100110011[ 0011] \dots_{2}

Limitation 2#

  • Just one setting of binary point within the ww bits
    • Limited range of numbers (very small values? very large? we have to move the binary point to represent sort of wide as wide a range as possible with as much precision given the number of bits)

Definition: IEEE Floating Point Standard#

IEEE Standard 754#

Established in 1985 as uniform standard for floating point arithmetic. Before that, many idiosyncratic formats.

Although it provided nice standards for rounding, overflow, underflow… It is hard to make fast in hardware (Numerical analysts predominated over hardware designers in defining standard)

Floating Point Representation#

Numerical form: (1)sM2E( -1)^{s} \cdot M\cdot 2^{E}

  • Sign bit s determines whether number is negative or positive
  • Significand M (Mantissa) normally a fractional value in range [1.0,2.0)[ 1.0,2.0)
  • Exponent E weights value by power of two

Encoding#

  • MSB s is sign bit s
  • exp field encodes E (but is not equal to E)
  • frac field encodes M (but is not equal to M)

Normalized Values#

When: exp0000exp\neq 000\dotsc 0 and exp1111exp\neq 111\dotsc 1

  • Exponent coded as a biased value: E=expbiasE=exp-bias
    • expexp is unsigned value of exp field
    • bias=2k11bias=2^{k-1} -1, where kk is number of exponent bits
      • Single precision: 127127 (exp[1,254]exp\in [ 1,254], E[126,127]E\in [ -126,127])
      • Double precision: 10231023 (exp[1,2046]exp\in [ 1,2046], E[1022,1023]E\in [ -1022,1023])
  • Significand coded with implied leading 11: M=1.xxxx2M=1.xxx\dotsc x_{2}
    • xxxxxxx\dotsc x is bits of frac field
    • Minimum when frac=0000 (M=1.0)frac=000\dotsc 0\ ( M=1.0)
    • Maximum when frac=1111 (M=2.0ϵ)frac=111\dotsc 1\ ( M=2.0-\epsilon )
    • Get extra leading bit for “free”
Normalized Encoding Example#
  • Value: float F = 15213.0;

    • 1521310=111011011011012=1.1101101101101×21315213_{10} =11101101101101_{2} =1.1101101101101\times 2^{13}
  • Significand

    • M=(1.)11011011011012M=( 1.) 1101101101101_{2}
    • frac=110110110110100000000002frac=11011011011010000000000_{2}
  • Exponent

    • E=13E=13
    • bias=127bias=127
    • exp=140=100011002exp=140=10001100_{2}

So the result would be:

# manually calculate
((-1)**0)*(1+1/2+1/4+1/16+1/32+1/128+1/256+1/1024+1/2048+1/8192)*(2**13) == 15213.0
# by struct package
import struct
bits = 0b01000110011011011011010000000000
f = struct.unpack("f", struct.pack("I", bits))[0]
print(f)

Denormalized Values#

  • Condition: exp=0000exp=000\dotsc 0
  • Exponent value: E=1bias (instead of E=0bias)E=1-bias\ ( instead\ of\ E=0-bias)
  • Significand coded with implied leading 00: M=0.xxxx2M=0.xxx\dotsc x_{2}
    • xxxxxxx\dotsc x is bits of frac field
  • Cases
    • exp=0000,frac=0000exp=000\dotsc 0,frac=000\dotsc 0
      • Represents zero value
      • Note distinct values: +0+0 and 0-0
    • exp=0000,farc0000exp=000\dotsc 0,farc\neq 000\dotsc 0
      • Numbers closet to 0.00.0
      • Equispaced

Special Values#

  • Condition: exp=1111exp=111\dotsc 1

  • Case: exp=1111,frac=0000exp=111\dotsc 1,frac=000\dotsc 0

    • Represents value \infty
    • Operation that overflows
    • Both positive and negative
    • E.g., 1.0/0.0=1.0/0.0=+,1.0/0.0=1.0/0.0=-1.0/-0.0=+\infty ,1.0/-0.0=-\infty
  • Case: exp=1111,frac0000exp=111\dotsc 1,frac\neq 000\dotsc 0

    • Not-a-Number (NaN)
    • Represents case when no numeric value can be determined
    • E.g., 1,,0\sqrt{-1} ,\infty -\infty ,\infty \cdot 0

Visualization: Floating Point Encodings#

Example and properties#

Tiny Floating Point Example#

Think about this 8-bit Floating Point Representation below, it obeying the same general form as IEEE Format:

Dynamic Range (Positive Only)#

Distribution of Values#

Still think about our tiny example, notice how the distribution gets denser toward zero.

Here is a scaled close-up view:

Special Properties of the IEEE Encoding#

  • FP Zero Same as Integer Zero
    • All bits = 0
  • Can (Almost) Use Unsigned Integer Comparison
    • Must first compare sign bits
    • Must consider 0=0-0=0
    • NaNs problematic
      • What should comparison yield?
    • Otherwise OK
      • Denormalized vs. Normalized
      • Normalized vs. Infinity

Rounding, addition, multiplication#

Floating Point Operations: Basic Idea#

x+fy=round(x+y)x+_{f} y=round( x+y)

x×fy=round(x×y)x\times _{f} y=round( x\times y)

Basic idea#

  • First compute exact result
  • Make it fit into desired precision
    • Possibly overflow if exponent too large
    • Possibly round to fit into frac

Rounding#

Rounding Modes (illustrate with $ rounding)

$1.40$1.60$1.50$2.50-$1.50
Towards Zero$1$1$1$2-$1
Round Down (-\infty )$1$1$1$2-$2
Round Up (++\infty )$2$2$2$3-$1
Nearest Even (default)$1$2$2$2-$2
Round to Even

It means, if you have a value that’s less than half way then you round down, if more than half way, round up. When you have something that’s exactly half way, then what you do is round towards the nearest even number.

Closer Look at Round-To-Even#

Default Rounding Mode#
  • Hard to get any other kind without dropping into assembly
  • All others are statistically biased
    • Sum of set of positive numbers will consistently be over or underestimated
Applying to Other Decimal Places / Bit Positions#
  • When exactly half way between two possible values
    • Round so that least significant digit is even

E.g., round to nearest hundredth:

ValueRounded
7.89499997.89Less than half way
7.89500017.90Greater than half way
7.89500007.90Half way (round up)
7.88500007.88Half way (rond down)

References#

The CSAPP Notebook
https://assembly.rip/posts/cs-notes/csapp/
Author
CuB3y0nd
Published at
2025-07-16
License
CC BY-NC-SA 4.0