Compare commits

...

7 Commits

Author SHA1 Message Date
a67a3701be typo
Some checks failed
CI / Typst formatting (pull_request) Successful in 6s
CI / Typos (pull_request) Failing after 7s
CI / Build (pull_request) Has been skipped
2025-02-09 13:15:00 -08:00
646d5467a3 Remove slide rule
Some checks failed
CI / Typst formatting (pull_request) Successful in 4s
CI / Typos (pull_request) Failing after 11s
CI / Build (pull_request) Has been skipped
2025-02-09 12:43:15 -08:00
edcb5179a3 Quake edits 2025-02-09 12:43:15 -08:00
e92fae453e edits 2025-02-09 12:43:15 -08:00
4f4675a164 Quake edits 2025-02-09 12:43:15 -08:00
6e49a6938b Fix imports 2025-02-09 12:43:15 -08:00
e67151f30a FIR draft 2025-02-09 12:43:15 -08:00
5 changed files with 485 additions and 0 deletions

View File

@ -0,0 +1,24 @@
% use [nosolutions] flag to hide solutions.
% use [solutions] flag to show solutions.
\documentclass[
singlenumbering,
solutions
]{../../../lib/tex/ormc_handout}
\usepackage{../../../lib/tex/macros}
\usepackage{listings}
\uptitlel{Advanced 2}
\uptitler{\smallurl{}}
\title{Fast Inverse Square Root}
\subtitle{Prepared by Mark on \today}
\begin{document}
\maketitle
\input{parts/1 int.tex}
\input{parts/2 float.tex}
\input{parts/3 approximate.tex}
\input{parts/4 quake.tex}
\end{document}

View File

@ -0,0 +1,101 @@
\section{Integers}
\definition{}
A \textit{bit string} is a string of binary digits. \par
In this handout, we'll denote bit strings with the prefix \texttt{0b}. \par
That is, $1010 =$ \say{one thousand and one,} while $\texttt{0b1001} = 2^3 + 2^0 = 9$
\vspace{2mm}
We will seperate long bit strings with underscores for readability. \par
Underscores have no meaning: $\texttt{0b1111\_0000} = \texttt{0b11110000}$.
\problem{}
What is the value of the following bit strings, if we interpret them as integers in base 2? \par
\begin{itemize}
\item \texttt{0b0001\_1010}
\item \texttt{0b0110\_0001}
\end{itemize}
\begin{solution}
\begin{itemize}
\item $\texttt{0b0001\_1010} = 2 + 8 + 16 = 26$
\item $\texttt{0b0110\_0001} = 1 + 32 + 64 = 95$
\end{itemize}
\end{solution}
\vfill
\pagebreak
\definition{}
We can interpret a bit string in any number of ways. \par
One such interpretation is the \textit{signed integer}, or \texttt{int} for short. \par
\texttt{ints} allow us to represent negative and positive integers using 32-bit strings.
\vspace{2mm}
The first bit of an \texttt{int} tells us its sign:
\begin{itemize}
\item if the first bit is \texttt{1}, the \textit{int} represents a negative number;
\item if the first bit is \texttt{0}, it represents a positive number.
\end{itemize}
We do not need negative numbers today, so we will assume that the first bit is always zero. \par
\note{If you'd like to know how negative integers are written, look up \say{two's complement} after class.}
\vspace{2mm}
The value of a positive signed \texttt{long} is simply the value of its binary digits:
\begin{itemize}
\item $\texttt{0b00000000\_00000000\_00000000\_00000000} = 0$
\item $\texttt{0b00000000\_00000000\_00000000\_00000011} = 3$
\item $\texttt{0b00000000\_00000000\_00000000\_00100000} = 32$
\item $\texttt{0b00000000\_00000000\_00000000\_10000010} = 130$
\end{itemize}
\problem{}
What is the largest number we can represent with a 32-bit \texttt{int}?
\begin{solution}
$\texttt{0b01111111\_11111111\_11111111\_11111111} = 2^{31}$
\end{solution}
\vfill
\problem{}
What is the smallest possible number we can represented with a 32-bit \texttt{int}? \par
\hint{
You do not need to know \textit{how} negative numbers are represented. \par
Assume that we do not skip any integers, and don't forget about zero.
}
\begin{solution}
There are $2^{64}$ possible 32-bit patterns,
of which 1 represents zero and $2^{31}$ represent positive numbers.
We therefore have access to $2^{64} - 1 - 2^{31}$ negative numbers,
giving us a minimum representable value of $-2^{31} + 1$.
\end{solution}
\vfill
\problem{}
Find the value of each of the following 32-bit \texttt{int}s:
\begin{itemize}
\item \texttt{0b00000000\_00000000\_00000101\_00111001}
\item \texttt{0b00000000\_00000000\_00000001\_00101100}
\item \texttt{0b00000000\_00000000\_00000100\_10110000}
\end{itemize}
\hint{The third conversion is easy---look carefully at the second.}
\begin{solution}
\begin{itemize}
\item $\texttt{0b00000000\_00000000\_00000101\_00111001} = 1337$
\item $\texttt{0b00000000\_00000000\_00000001\_00101100} = 300$
\item $\texttt{0b00000000\_00000000\_00000010\_01011000} = 1200$
\end{itemize}
Notice that the third long is the second shifted left twice (i.e, multiplied by 4)
\end{solution}
\vfill
\vfill
\pagebreak

View File

@ -0,0 +1,186 @@
\section{Floats}
\definition{}
\textit{Binary decimals}\footnotemark{} are very similar to base-10 decimals. \par
In base 10, we interpret place value as follows:
\begin{itemize}
\item $0.1 = 10^{-1}$
\item $0.03 = 3 \ \times 10^{-2}$
\item $0.0208 = 2 \times 10^{-2} + 8 \times 10^{-4}$
\end{itemize}
\footnotetext{\say{decimal} is a misnomer, but that's ok.}
\vspace{5mm}
We can do the same in base 2:
\begin{itemize}
\item $\texttt{0.1} = 2^{-1} = 0.5$
\item $\texttt{0.011} = 2^{-2} + 2^{-3} = 0.375$
\item $\texttt{101.01} = 5.125$
\end{itemize}
\vspace{5mm}
\problem{}
Rewrite the following binary decimals in base 10: \par
\note{You may leave your answer as a fraction.}
\begin{itemize}
\item $\texttt{1011.101}$
\item $\texttt{110.1101}$
\end{itemize}
\vfill
\pagebreak
\definition{}
Another way we can interpret a bit string is as a \textit{signed floating-point decimal}, or a \texttt{float} for short. \par
Floats represent a subset of the real numbers, and are interpreted as follows: \par
\note{The following only applies to floats that consist of 32 bits. We won't encounter any others today.}
\begin{center}
\begin{tikzpicture}
\node[anchor=south west] at (0, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (0.25, 0) {\texttt{\texttt{b}}};
\node[anchor=south west] at (0.50, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (0.75, 0) {\texttt{\texttt{\_}}};
\node[anchor=south west] at (1.00, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (1.25, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (1.50, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (1.75, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (2.00, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (2.25, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (2.50, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (2.75, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (3.00, 0) {\texttt{\texttt{\_}}};
\node[anchor=south west] at (3.25, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (3.50, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (3.75, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (4.00, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (4.25, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (4.50, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (4.75, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (5.00, 0) {\texttt{\texttt{\_}}};
\node[anchor=south west] at (5.25, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (5.50, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (5.75, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (6.00, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (6.25, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (6.50, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (6.75, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (7.00, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (7.25, 0) {\texttt{\texttt{\_}}};
\node[anchor=south west] at (7.50, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (7.75, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (8.00, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (8.25, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (8.50, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (8.75, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (9.00, 0) {\texttt{\texttt{0}}};
\node[anchor=south west] at (9.25, 0) {\texttt{\texttt{0}}};
\draw (0.50, 0) -- (0.95, 0) node [midway, below=1mm] {sign};
\draw (1.05, 0) -- (3.15, 0) node [midway, below=1mm] {exponent};
\draw (3.30, 0) -- (9.70, 0) node [midway, below=1mm] {fraction};
\end{tikzpicture}
\end{center}
\begin{itemize}[itemsep = 2mm]
\item The first bit denotes the sign of the float's value. We'll label it $s$. \par
If $s = \texttt{1}$, this float is negative; if $s = \texttt{0}$, it is positive.
\item The next eight bits represent the \textit{exponent} of this float. \note{(we'll see what that means soon)}\par
We'll call the value of this eight-bit binary integer $E$. \par
Naturally, $0 \leq E \leq 255$ \note{(since $E$ consist of eight bits.)}
\item The remaining 23 bits represent the \textit{fraction} of this float, which we'll call $F$. \par
These 23 bits are interpreted as the fractional part of a binary decimal. \par
For example, the bits \texttt{0b1010000\_00000000\_00000000} represents $0.5 + 0.125 = 0.625$.
\end{itemize}
\problem{}<floata>
Consider \texttt{0b01000001\_10101000\_00000000\_00000000}. \par
Find the $s$, $E$, and $F$ we get if we interpret this bit string as a \texttt{float}. \par
\note[Note]{Leave $F$ as a sum of powers of two.}
\begin{solution}
$s = 0$ \par
$E = 258$ \par
$F = 2^{31}+2^{19} = 2,621,440$
\end{solution}
\vfill
\definition{}
The final value of a float with sign $s$, exponent $E$, and fraction $F$ is
\begin{equation*}
(-1)^s ~\times~ 2^{E - 127} ~\times~ \left(1 + \frac{F}{2^{23}}\right)
\end{equation*}
Notice that this is very similar to decimal scientific notation, which is written as
\begin{equation*}
(-1)^s ~\times~ 10^{e} ~\times~ (f)
\end{equation*}
\problem{}
Consider \texttt{0b01000001\_10101000\_00000000\_00000000}. \par
This is the same bit string we used in \ref{floata}. \par
\vspace{2mm}
What value do we get if we interpret this bit string as a float? \par
\hint{$21 \div 16 = 1.3125$}
\begin{solution}
This is 21:
\begin{equation*}
2^{131} \times \biggl(1 + \frac{2^{21} + 2^{19}}{2^{23}}\biggr)
~=~ 2^{4} \times (1 + 0.25 + 0.0625)
~=~ 16 \times (1.3125)
~=~ 21
\end{equation*}
\end{solution}
\vfill
\pagebreak
\problem{}
Encode $12.5$ as a float. \par
\hint{$12.5 \div 8 = 1.5625$}
\begin{solution}
\begin{equation*}
12.5
~=~ 8 \times 1.5625
~=~ 2^{3} \times \biggl(1 + (0.5 + 0.0625)\biggr)
~=~ 2^{130} \times \biggl(1 + \frac{2^{22} + 2^{19}}{2^{23}}\biggr)
\end{equation*}
which is \texttt{0b01000001\_01001000\_00000000\_00000000}. \par
\end{solution}
\vfill
\definition{}
Say we have a bit string $x$. \par
We'll let $x_f$ denote the value we get if we interpret $x$ as a float, \par
and we'll let $x_i$ denote the value we get if we interpret $x$ an integer.
\problem{}
Let $x = \texttt{0b01000001\_01001000\_00000000\_00000000}$. \par
What are $x_f$ and $x_i$? \note{As always, you may leave big numbers as powers of two.}
\begin{solution}
$x_f = 12.5$ \par
\vspace{2mm}
$x_i = 2^{30} + 2^{24} + 2^{22} + 2^{19} = 11,095,237,632$
\end{solution}
\vfill
\pagebreak

View File

@ -0,0 +1,42 @@
\section{Integers and Floats}
\generic{Observation:}
For small values of $a$, $\log_2(1 + a)$ is approximately equal to $a$. \par
Note that this equality is exact for $a = 0$ and $a = 1$, since $\log_2(1) = 0$ and $\log_2(2) = 1$.
\vspace{2mm}
We'll add a \say{correction term} $\varepsilon$ to this approximation, so that $\log_2(1 + a) \approx a + \varepsilon$.
TODO: why? Graphs.
\problem{}<convert>
Use the fact that $\log_2(1 + a) \approx a + \varepsilon$ to approximate $\log_2(x_f)$ in terms of $x_i$. \par
\vspace{5mm}
Namely, show that
\begin{equation*}
\log_2(x_f) ~=~ \frac{x_i}{2^{23}} - 127 + \varepsilon
\end{equation*}
for some correction term term $\varepsilon$ \par
\note{
In other words, we're finding an expression for $x$ as a float
in terms of $x$ as an int.
}
\begin{solution}
Let $E$ and $F$ be the exponent and float bits of $x_f$. \par
We then have:
\begin{align*}
\log_2(x_f)
&=~ \log_2 \left( 2^{E-127} \times \left(1 + \frac{F}{2^{23}}\right) \right) \\
&=~ E-127 + \log_2\left(1 + \frac{F}{2^{23}}\right) \\
&\approx~ E-127 + \frac{F}{2^{23}} + \varepsilon \\
&=~ \frac{1}{2^{23}}(2^{23}E + F) - 127 + \varepsilon \\
&=~ \frac{1}{2^{23}}(x_i) - 127 + \varepsilon
\end{align*}
\end{solution}
\vfill
\pagebreak

View File

@ -0,0 +1,132 @@
\section{The Fast Inverse Square Root}
The following code is present in \textit{Quake III Arena} (1999):
\lstset{
breaklines=false,
numbersep=5pt,
xrightmargin=0in
}
\begin{lstlisting}[language=C]
float Q_rsqrt( float number ) {
long i = * ( long * ) &number;
i = 0x5f3759df - ( i >> 1 );
return * ( float * ) &i;
}
\end{lstlisting}
This code defines a function \texttt{Q\_rsqrt} that consumes a float named
\texttt{number} and approximates its inverse square root (in other words, \texttt{Q\_rsqrt} computes $1/\sqrt{\texttt{number}}$).
\vspace{2mm}
If we rewrite this using notation we're familiar with, we get the following:
\begin{equation*}
\texttt{Q\_sqrt}(n_f) = 6240089 - (n_i \div 2) \approx \frac{1}{\sqrt{n_f}}
\end{equation*}
\note{
\texttt{0x5f3759df} is $6240089$ in hexadecimal. \par
It is a magic number hard-coded into \texttt{Q\_sqrt}.
}
\vspace{2mm}
Our goal in this section is to understand why this works: \par
\begin{itemize}
\item How does Quake approximate $\frac{1}{\sqrt{x}}$ by simply subtracting and dividing by two?
\item What's special about $6240089$?
\end{itemize}
\problem{}
Using basic log rules, rewrite $\log_2(1 / \sqrt{x})$ in terms of $\log_2(x)$.
\begin{solution}
\begin{equation*}
\log_2(1 / \sqrt{x}) = \frac{-1}{2}\log_2(x)
\end{equation*}
\end{solution}
\vfill
\pagebreak
\generic{Setup:}
We are now ready to show that $\texttt{Q\_sqrt} \approx \frac{1}{\sqrt{n_f}}$. \par
For convenience, let's call the bit string of the inverse square root $r$. \par
In other words,
\begin{equation*}
r_f := \frac{1}{\sqrt{n_f}}
\end{equation*}
This is the value we want to approximate.
\problem{}<finala>
Find an approximation for $\log_2(r_f)$ in terms of $n_i$ and $\varepsilon$ \par
\note{Remember, $\varepsilon$ is the correction constant in our approximation of $\log_2(1 + a)$.}
\begin{solution}
\begin{equation*}
\log_2(r_f)
~=~ \log_2(\frac{1}{\sqrt{n_f}})
~=~ \frac{-1}{2}\log_2(n_f)
~\approx~ \frac{-1}{2}\left( \frac{n_i}{2^{23}} + \varepsilon - 127 \right)
\end{equation*}
\end{solution}
\vfill
\problem{}<finalb>
Let's call the \say{magic number} in the code above $\kappa$, so that
\begin{equation*}
\texttt{Q\_sqrt}(n_f) = \kappa - (n_i \div 2)
\end{equation*}
Use problems \ref{num:convert} and \ref{num:finala} to show that $\texttt{Q\_sqrt}(n_f) \approx r_i$. \par
\begin{solution}
From \ref{convert}, we know that
\begin{equation*}
\log_2(r_f) \approx \frac{r_i}{2^{23}} + \varepsilon - 127
\end{equation*}
\note{
Our approximation of $\log_2(1+a)$ uses a fixed correction constant, \par
so the $\varepsilon$ here is equivalent to the $\varepsilon$ in \ref{finala}.
}
Combining this with the result from \ref{finala}, we get:
\begin{align*}
\frac{r_i}{2^{23}} + \varepsilon - 127
&~\approx~\frac{-1}{2}\left( \frac{n_i}{2^{23}} + \varepsilon - 127 \right) \\
\frac{r_i}{2^{23}}
&~\approx~\frac{-1}{2}\left( \frac{n_i}{2^{23}} \right) + \frac{3}{2}(\varepsilon - 127) \\
r_i
&~\approx~\frac{-1}{2}\left(n_i \right) + 2^{23}\frac{3}{2}(\varepsilon - 127)
~=~ 2^{23}\frac{3}{2}(\varepsilon - 127) - \frac{n_i}{2}
\end{align*}
\vspace{2mm}
This is exactly what we need! If we set $\kappa$ to $\frac{2^{24}}{3}(\varepsilon - 127)$, then
\begin{equation*}
r_i ~\approx~ \kappa - (n_i \div 2) ~=~ \texttt{Q\_sqrt}(n_f)
\end{equation*}
\end{solution}
\vfill
\problem{}
What is the exact value of $\kappa$ in terms of $\varepsilon$? \par
\hint{Look at \ref{finalb}. We already found it!}
\begin{solution}
This problem makes sure our students see that
$\kappa = \frac{2^{24}}{3}(\varepsilon - 127)$. \par
See the solution to \ref{finalb}.
\end{solution}
\makeatletter
\if@solutions\else
\vspace{2cm}
\fi\makeatother
\pagebreak