Compare commits

...

2 Commits

Author SHA1 Message Date
a10774c116 Add slide rule warm-up
All checks were successful
CI / Typos (pull_request) Successful in 11s
CI / Typst formatting (pull_request) Successful in 6s
CI / Build (pull_request) Successful in 9m17s
2025-02-13 15:34:15 -08:00
b4852e7fcd Tom edits 2025-02-13 15:34:06 -08:00
15 changed files with 25260 additions and 34 deletions

View File

@ -13,16 +13,19 @@
by: "Mark",
)
#include "parts/00 int.typ"
#include "parts/00 intro.typ"
#pagebreak()
#include "parts/01 float.typ"
#include "parts/01 int.typ"
#pagebreak()
#include "parts/02 approx.typ"
#include "parts/02 float.typ"
#pagebreak()
#include "parts/03 quake.typ"
#include "parts/03 approx.typ"
#pagebreak()
#include "parts/04 bonus.typ"
#include "parts/04 quake.typ"
#pagebreak()
#include "parts/05 bonus.typ"

View File

@ -0,0 +1,45 @@
#import "@local/handout:0.1.0": *
= Introduction
In 2005, ID Software published the source code of _Quake III Arena_, a popular game released in 1999. \
This caused quite a stir: ID Software was responsible for many games popular among old-school engineers (most notably _Doom_, which has a place in programmer humor even today).
#v(2mm)
Naturally, this community immediately began dissecting _Quake_'s source. \
One particularly interesting function is reproduced below, with original comments: \
#v(3mm)
```c
float Q_rsqrt( float number ) {
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // [redacted]
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
```
#v(3mm)
This code defines a function `Q_sqrt`, which was used as a fast approximation of the inverse square root in graphics routines. (in other words, `Q_sqrt` efficiently approximates $1 div sqrt(x)$)
#v(3mm)
The key word here is "fast": _Quake_ ran on very limited hardware, and traditional approximation techniques (like Taylor series)#footnote[Taylor series aren't used today, and for the same reason. There are better ways.] were too computationally expensive to be viable.
#v(3mm)
Our goal today is to understand how `Q_sqrt` works. \
To do that, we'll first need to understand how computers represent numbers. \
We'll start with simple binary integers---turn the page.

View File

@ -5,7 +5,8 @@
#definition()
A _bit string_ is a string of binary digits. \
In this handout, we'll denote bit strings with the prefix `0b`. \
That is, $1010 =$ "one thousand and one," while $#text([`0b1001`]) = 2^3 + 2^0 = 9$
#note[This prefix is only notation---it is _not_ part of the string itself.] \
For example, $1001$ is the number "one thousand and one," while $#text([`0b1001`])$ is the string of bits "1 0 0 1".
#v(2mm)
We will separate long bit strings with underscores for readability. \
@ -40,7 +41,7 @@ The value of a `uint` is simply its value as a binary number:
What is the largest number we can represent with a 32-bit `uint`?
#solution([
$#text([`0b01111111_11111111_11111111_11111111`]) = 2^(31)$
$#text([`0b11111111_11111111_11111111_11111111`]) = 2^(32)-1$
])
#v(1fr)
@ -53,6 +54,10 @@ Find the value of each of the following 32-bit unsigned integers:
- `0b00000000_00000000_00000100_10110000`
#hint([The third conversion is easy---look carefully at the second.])
#instructornote[
Consider making a list of the powers of two $>= 1024$ on the board.
]
#solution([
- $#text([`0b00000000_00000000_00000101_00111001`]) = 1337$
- $#text([`0b00000000_00000000_00000001_00101100`]) = 300$
@ -64,20 +69,20 @@ Find the value of each of the following 32-bit unsigned integers:
#definition()
In general, division of `uints` is nontrivial#footnote([One may use repeated subtraction, but that isn't efficient.]). \
In general, fast division of `uints` is difficult#footnote([One may use repeated subtraction, but this isn't efficient.]). \
Division by powers of two, however, is incredibly easy: \
To divide by two, all we need to do is shift the bits of our integer right.
#v(2mm)
For example, consider $#text[`0b0000_0110`] = 6$. \
If we insert a zero at the left end of this bit string and delete the digit at the right \
If we insert a zero at the left end of this string and delete the zero at the right \
(thus "shifting" each bit right), we get `0b0000_0011`, which is 3. \
#v(2mm)
Of course, we loose the remainder when we left-shift an odd number: \
$9 div 2 = 4$, since `0b0000_1001` shifted right is `0b0000_0100`.
Of course, we lose the remainder when we right-shift an odd number: \
$9$ shifted right is $4$, since `0b0000_1001` shifted right is `0b0000_0100`.
#problem()
Right shifts are denoted by the `>>` symbol: \
@ -86,6 +91,7 @@ Find the value of the following:
- $12 #text[`>>`] 1$
- $27 #text[`>>`] 3$
- $16 #text[`>>`] 8$
#note[Naturally, you'll have to convert these integers to binary first.]
#solution[
- $12 #text[`>>`] 1 = 6$

View File

@ -3,7 +3,7 @@
= Floats
#definition()
_Binary decimals_#footnote["decimal" is a misnomer, but that's ok.] are very similar to base-10 decimals. \
_Binary decimals_#footnote([Note that "binary decimal" is a misnomer---"deci" means "ten"!]) are very similar to base-10 decimals.\
In base 10, we interpret place value as follows:
- $0.1 = 10^(-1)$
- $0.03 = 3 times 10^(-2)$
@ -107,11 +107,13 @@ Floats represent a subset of the real numbers, and are interpreted as follows: \
- The next eight bits represent the _exponent_ of this float.
#note([(we'll see what that means soon)]) \
We'll call the value of this eight-bit binary integer $E$. \
Naturally, $0 <= E <= 255$ #note([(since $E$ consist of eight bits.)])
Naturally, $0 <= E <= 255$ #note([(since $E$ consist of eight bits)])
- The remaining 23 bits represent the _fraction_ of this float, which we'll call $F$. \
These 23 bits are interpreted as the fractional part of a binary decimal. \
For example, the bits `0b10100000_00000000_00000000` represents $0.5 + 0.125 = 0.625$.
- The remaining 23 bits represent the _fraction_ of this float. \
They are interpreted as the fractional part of a binary decimal. \
For example, the bits `0b10100000_00000000_00000000` represent $0.5 + 0.125 = 0.625$. \
We'll call the value of these bits as a binary integer $F$. \
Their value as a binary decimal is then $F div 2^23$. #note([(convince yourself of this)])
#problem(label: "floata")
@ -135,12 +137,17 @@ $
(-1)^s times 2^(E - 127) times (1 + F / (2^(23)))
$
Notice that this is very similar to decimal scientific notation, which is written as
Notice that this is very similar to base-10 scientific notation, which is written as
$
(-1)^s times 10^(e) times (f)
$
#note[
We subtract 127 from $E$ so we can represent positive and negative numbers. \
$E$ is an eight bit binary integer, so $0 <= E <= 255$ and thus $-127 <= (E - 127) <= 127$.
]
#problem()
Consider `0b01000001_10101000_00000000_00000000`. \
This is the same bit string we used in @floata. \

View File

@ -5,7 +5,7 @@
= Integers and Floats
#generic("Observation:")
For small values of $x$, $log_2(1 + x)$ is approximately equal to $x$. \
If $x$ is smaller than 1, $log_2(1 + x)$ is approximately equal to $x$. \
Note that this equality is exact for $x = 0$ and $x = 1$, since $log_2(1) = 0$ and $log_2(2) = 1$.
#v(5mm)
@ -18,7 +18,7 @@ This allows us to improve the average error of our linear approximation:
align: center,
columns: (1fr, 1fr),
inset: 5mm,
[$log(1+x)$ and $x + 0$]
[$log_2(1+x)$ and $x + 0$]
+ cetz.canvas({
import cetz.draw: *
@ -64,7 +64,7 @@ This allows us to improve the average error of our linear approximation:
Max error: 0.086 \
Average error: 0.0573
],
[$log(1+x)$ and $x + 0.045$]
[$log_2(1+x)$ and $x + 0.045$]
+ cetz.canvas({
import cetz.draw: *
@ -125,7 +125,7 @@ We won't bother with this---we'll simply leave the correction term as an opaque
[
"Average error" above is simply the area of the region between the two graphs:
$
integral_0^1 abs( #v(1mm) log(1+x) - (x+epsilon) #v(1mm))
integral_0^1 abs( #v(1mm) log(1+x)_2 - (x+epsilon) #v(1mm))
$
Feel free to ignore this note, it isn't a critical part of this handout.
],

View File

@ -2,10 +2,9 @@
= The Fast Inverse Square Root
A simplified version of the _Quake_ routine we are studying is reproduced below.
The following code is present in _Quake III Arena_ (1999):
#v(5mm)
#v(2mm)
```c
float Q_rsqrt( float number ) {
@ -15,20 +14,20 @@ float Q_rsqrt( float number ) {
}
```
#v(5mm)
#v(2mm)
This code defines a function `Q_rsqrt` that consumes a float `number` and approximates its inverse square root.
If we rewrite this using notation we're familiar with, we get the following:
$
#text[`Q_sqrt`] (n_f) =
#h(5mm)
6240089 - (n_i div 2)
#h(5mm)
#h(10mm)
approx 1 / sqrt(n_f)
$
#note[
`0x5f3759df` is $6240089$ in hexadecimal. \
Ask an instructor to explain if you don't know what this means. \
It is a magic number hard-coded into `Q_sqrt`.
]
@ -56,7 +55,7 @@ For those that are interested, here are the details of the "code-to-math" transl
- Notice the right-shift in the second line of the function. \
We translated `(i >> i)` into $(n_i div 2)$.
We translated `(i >> 1)` into $(n_i div 2)$.
#v(2mm)
- "`return * (float *) &i`" is again C magic. \
@ -64,17 +63,17 @@ For those that are interested, here are the details of the "code-to-math" transl
#pagebreak()
#generic("Setup:")
We are now ready to show that $#text[`Q_sqrt`] (x) approx 1/sqrt(x)$. \
We are now ready to show that $#text[`Q_sqrt`] (x)$ effectively approximates $1/sqrt(x)$. \
For convenience, let's call the bit string of the inverse square root $r$. \
In other words,
$
r_f := 1 / (sqrt(n_f))
$
This is the value we want to approximate.
This is the value we want to approximate. \
#problem(label: "finala")
Find an approximation for $log_2(r_f)$ in terms of $n_i$ and $epsilon$ \
#note[Remember, $epsilon$ is the correction constant in our approximation of $log_2(1 + a)$.]
#note[Remember, $epsilon$ is the correction constant in our approximation of $log_2(1 + x)$.]
#solution[
$
@ -92,7 +91,11 @@ Let's call the "magic number" in the code above $kappa$, so that
$
#text[`Q_sqrt`] (n_f) = kappa - (n_i div 2)
$
Use @convert and @finala to show that $#text[`Q_sqrt`] (n_f) approx r_i$
Use @convert and @finala to show that $#text[`Q_sqrt`] (n_f) approx r_i$ \
#note(type: "Note")[
If we know $r_i$, we know $r_f$. \
We don't even need to convert between the two---the underlying bits are the same!
]
#solution[
From @convert, we know that
@ -164,8 +167,7 @@ though it is fairly close to the ideal $epsilon$.
#remark()
And now, we're done! \
We've shown that `Q_sqrt(x)` approximates $1/sqrt(x)$ fairly well, \
thanks to the approximation $log(1+a) = a + epsilon$.
We've shown that `Q_sqrt(x)` approximates $1/sqrt(x)$ fairly well. \
#v(2mm)

View File

@ -0,0 +1,78 @@
% use [nosolutions] flag to hide solutions.
% use [solutions] flag to show solutions.
\documentclass[
solutions,
shortwarning
]{../../../lib/tex/ormc_handout}
\usepackage{../../../lib/tex/macros}
\usepackage{pdfpages}
\usepackage{sliderule}
\usepackage{changepage}
% Args:
% x, top scale y, label
\newcommand{\slideruleind}[3]{
\draw[
line width=1mm,
draw=black,
opacity=0.3,
text opacity=1
]
({#1}, {#2 + 1})
--
({#1}, {#2 - 1.1})
node [below] {#3};
}
\uptitlel{Advanced}
\uptitler{\smallurl{}}
\title{Warm-Up: Slide Rules}
\subtitle{Prepared by Mark on \today}
\begin{document}
\maketitle
\begin{center}
\begin{minipage}{6cm}
Dad says that anyone who can't use
a slide rule is a cultural illiterate
and should not be allowed to vote.
\vspace{1ex}
\textit{Have Space Suit --- Will Travel, 1958}
\end{minipage}
\end{center}
\hfill
\input{parts/0 logarithms.tex}
\input{parts/1 intro.tex}
\input{parts/2 multiplication.tex}
% Make sure the slide rule is on an odd page,
% so that double-sided printing won't require
% students to tear off problems.
\checkoddpage
\ifoddpage\else
\vspace*{\fill}
\begin{center}
{
\Large
\textbf{This page unintentionally left blank.}
}
\end{center}
\vspace{\fill}
\pagebreak
\fi
\includepdf[
pages=1,
fitpaper=true
]{resources/rule.pdf}
\end{document}

View File

@ -0,0 +1,6 @@
[metadata]
title = "Slide Rules"
[publish]
handout = false
solutions = true

View File

@ -0,0 +1,59 @@
\section{Logarithms}
\problem{}
Evaluate the following by hand:
\begin{enumerate}
\item $\log_{10}{(1000)}$
\vfill
\item $\log_2{(64)}$
\vfill
\item $\log_2{(\frac{1}{4})}$
\vfill
\item $\log_x{(x)}$ for any $x$
\vfill
\item $log_x{(1)}$ for any $x$
\vfill
\end{enumerate}
\problem{}<logids>
Prove the following:
\begin{enumerate}[itemsep=2mm]
\item $\log_b{(b^x)} = x$
\vfill
\item $b^{\log_b{x}} = x$
\vfill
\item $\log_b{(xy)} = \log_b{(x)} + \log_b{(y)}$
\vfill
\item $\log_b{(\frac{x}{y})} = \log_b{(x)} - \log_b{(y)}$
\vfill
\item $\log_b{(x^y)} = y \log_b{(x)}$
\vfill
\end{enumerate}
\begin{instructornote}
A good intro to the following sections is the linear slide rule:
\note{Note that these rules start at 0.}
\begin{center}
\begin{tikzpicture}[scale=0.6]
\linearscale{2}{1}{}
\linearscale{0}{0}{}
\slideruleind
{5}
{1}
{2 + 3 = 5}
\end{tikzpicture}
\end{center}
Take two linear rules, offset one, and you add.
Do the same with a log scale, and you multiply! \\
\linehack{}
After assembling the paper slide rule, you can make a visor with some transparent tape. Wrap a strip around the slide rule, sticky side out, and stick it to itself to form a ring. Cover the sticky side with another layer of tape, and trim the edges to make them straight. Use the edge of the visor to read your slide rule!
\end{instructornote}
\pagebreak

View File

@ -0,0 +1,43 @@
\section{Introduction}
Mathematicians, physicists, and engineers needed to quickly compute products long before computers conquered the world.
\medskip
The \textit{slide rule} is an instrument that uses the logarithm to solve this problem. Before you continue, cut out and assemble your slide rule.
\medskip
There are four scales on your slide rule, each labeled with a letter on the left side:
\def\sliderulewidth{13}
\begin{center}
\begin{tikzpicture}[scale=1]
\tscale{0}{9}{T}
\kscale{0}{8}{K}
\abscale{0}{7}{A}
\abscale{0}{5.5}{B}
\ciscale{0}{4.5}{CI}
\cdscale{0}{3.5}{C}
\cdscale{0}{2}{D}
\lscale{0}{1}{L}
\sscale{0}{0}{S}
\end{tikzpicture}
\end{center}
Each scale's ``generating function'' is on the right:
\begin{itemize}
\item T: $\tan$
\item K: $x^3$
\item A,B: $x^2$
\item CI: $\frac{1}{x}$
\item C, D: $x$
\item L: $\log_{10}(x)$
\item S: $\sin$
\end{itemize}
Once you understand the layout of your slide rule, move on to the next page.
\pagebreak

View File

@ -0,0 +1,299 @@
\section{Multiplication}
We'll use the C and D scales of your slide rule to multiply. \\
Say we want to multiply $2 \times 3$. First, move the \textit{left-hand index} of the C scale over the smaller number, $2$:
\def\sliderulewidth{10}
\begin{center}
\begin{tikzpicture}[scale=1]
\cdscale{\cdscalefn(2)}{1}{C}
\cdscale{0}{0}{D}
\end{tikzpicture}
\end{center}
Then we'll find the second number, $3$ on the C scale, and read the D scale under it:
\begin{center}
\begin{tikzpicture}[scale=1]
\cdscale{\cdscalefn(2)}{1}{C}
\cdscale{0}{0}{D}
\slideruleind
{\cdscalefn(6)}
{1}
{6}
\end{tikzpicture}
\end{center}
Of course, our answer is 6.
\problem{}
What is $1.15 \times 2.1$? \\
Use your slide rule.
\begin{solution}
\begin{center}
\begin{tikzpicture}[scale=1]
\cdscale{\cdscalefn(1.15)}{1}{C}
\cdscale{0}{0}{D}
\slideruleind
{\cdscalefn(1.15)}
{1}
{1.15}
\slideruleind
{\cdscalefn(1.15) + \cdscalefn(2.1)}
{1}
{2.415}
\end{tikzpicture}
\end{center}
\end{solution}
\vfill
Note that your answer isn't exact. $1.15 \times 2.1 = 2.415$, but an answer accurate within two decimal places is close enough for most practical applications. \\
\pagebreak
Look at your C and D scales again. They contain every number between 1 and 10, but no more than that.
What should we do if we want to calculate $32 \times 210$? \\
\problem{}
Using your slide rule, calculate $32 \times 210$. \\
%\hint{$32 = 3.2 \times 10^1$}
\begin{solution}
\begin{center}
\begin{tikzpicture}[scale=1]
\cdscale{\cdscalefn(2.1)}{1}{C}
\cdscale{0}{0}{D}
\slideruleind
{\cdscalefn(2.1)}
{1}
{2.1}
\slideruleind
{\cdscalefn(2.1) + \cdscalefn(3.2)}
{1}
{6.72}
\end{tikzpicture}
\end{center}
Placing the decimal point correctly is your job. \\
$10^1 \times 10^2 = 10^3$, so our final answer is $6.72 \times 10^3 = 672$.
\end{solution}
\vfill
%This method of writing numbers is called \textit{scientific notation}. In the form $a \times 10^b$, $a$ is called the \textit{mantissa}, and $b$, the \textit{exponent}. \\
%You may also see expressions like $4.3\text{e}2$. This is equivalent to $4.3 \times 10^2$, but is more compact.
\problem{}
Compute the following:
\begin{enumerate}
\item $1.44 \times 52$
\item $0.38 \times 1.24$
\item $\pi \times 2.35$
\end{enumerate}
\begin{solution}
\begin{enumerate}
\item $1.44 \times 52 = 74.88$
\item $0.38 \times 1.24 = 0.4712$
\item $\pi \times 2.35 = 7.382$
\end{enumerate}
\end{solution}
\vfill
\pagebreak
\problem{}<provemult>
Note that the numbers on your C and D scales are logarithmically spaced.
\def\sliderulewidth{13}
\begin{center}
\begin{tikzpicture}[scale=1]
\cdscale{0}{1}{C}
\cdscale{0}{0}{D}
\end{tikzpicture}
\end{center}
Why does our multiplication procedure work? \\
%\hint{See \ref{logids}}
\vfill
\pagebreak
Now we want to compute $7.2 \times 5.5$:
\def\sliderulewidth{10}
\begin{center}
\begin{tikzpicture}[scale=0.8]
\cdscale{\cdscalefn(5.5)}{1}{C}
\cdscale{0}{0}{D}
\slideruleind
{\cdscalefn(5.5)}
{1}
{5.5}
\slideruleind
{\cdscalefn(5.5) + \cdscalefn(7.2)}
{1}
{???}
\end{tikzpicture}
\end{center}
No matter what order we go in, the answer ends up off the scale. There must be another way. \\
\medskip
Look at the far right of your C scale. There's an arrow pointing to the $10$ tick, labeled \textit{right-hand index}. Move it over the \textit{larger} number, $7.2$:
\begin{center}
\begin{tikzpicture}[scale=1]
\cdscale{\cdscalefn(7.2) - \cdscalefn(10)}{1}{C}
\cdscale{0}{0}{D}
\slideruleind
{\cdscalefn(7.2)}
{1}
{7.2}
\end{tikzpicture}
\end{center}
Now find the smaller number, $5.5$, on the C scale, and read the D scale under it:
\begin{center}
\begin{tikzpicture}[scale=1]
\cdscale{\cdscalefn(7.2) - \cdscalefn(10)}{1}{C}
\cdscale{0}{0}{D}
\slideruleind
{\cdscalefn(7.2)}
{1}
{7.2}
\slideruleind
{\cdscalefn(3.96)}
{1}
{3.96}
\end{tikzpicture}
\end{center}
Our answer should be about $7 \times 5 = 35$, so let's move the decimal point: $5.5 \times 7.2 = 39.6$. We can do this by hand to verify our answer. \\
\medskip
\problem{}
Why does this work? \par
\hint{Add a second $D$ scale.}
\begin{solution}
Consider the following picture, where I've put two D scales next to each other:
\begin{center}
\begin{tikzpicture}[scale=0.7]
\cdscale{\cdscalefn(7.2) - \cdscalefn(10)}{1}{C}
\cdscale{0}{0}{}
\cdscale{-10}{0}{}
\draw[
draw=black,
]
(0, 0)
--
(0, -0.3)
node [below] {D};
\draw[
draw=black,
]
(-10, 0)
--
(-10, -0.3)
node [below] {D};
\slideruleind
{-10 + \cdscalefn(7.2)}
{1}
{7.2}
\slideruleind
{\cdscalefn(7.2)}
{1}
{7.2}
\slideruleind
{\cdscalefn(3.96)}
{1}
{3.96}
\end{tikzpicture}
\end{center}
\medskip
The second D scale has been moved to the right by $(\log{10})$, so every value on it is $(\log{10})$ smaller than it should be.
\medskip
\medskip
In other words, the answer we get from reverse multiplication is the following: $\log{a} + \log{b} - \log{10}$. \\
This reduces to $\log{(\frac{a \times b}{10})}$, which explains the misplaced decimal point in $7.2 \times 5.5$.
\end{solution}
\vfill
\pagebreak
\problem{}
Compute the following using your slide rule:
\begin{enumerate}
\item $9 \times 8$
\item $15 \times 35$
\item $42.1 \times 7.65$
\item $6.5^2$
\end{enumerate}
\begin{solution}
\begin{enumerate}
\item $9 \times 8 = 72$
\item $15 \times 35 = 525$
\item $42.1 \times 7.65 = 322.065$
\item $6.5^2 = 42.25$
\end{enumerate}
\end{solution}
\vfill
\problem{}
Compute the following using your slide rule. \\
\begin{enumerate}
\item $135 \div 15$
\item $68.2 \div 0.575$
\item $(118 \times 0.51) \div 6.6$
\end{enumerate}
\begin{solution}
\begin{enumerate}
\item $135 \div 15 = 9$
\item $68.2 \div 0.575 = 118.609$
\item $(118 \times 0.51) \div 6.6 = 9.118$
\end{enumerate}
\end{solution}
\vfill
\pagebreak

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 862 KiB

View File

@ -0,0 +1,534 @@
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{sliderule}[2022/08/22 Slide rule tools]
\RequirePackage{tikz}
\RequirePackage{ifthen}
% Scale functions:
% See https://sliderulemuseum.com/SR_Scales.htm
%
% l: length of the rule
% n: the number on the rule
%
% A/B: (l/2) * log(n)
% C/D: l / log(n)
% CI: abs(l * log(10 / n) - l)
% K: (l/3) * log(n)
%
% L: n * l
% T: l * log(10 * tan(n))
% S: l * log(10 * sin(n))
\def\sliderulewidth{10}
\def\abscalefn(#1){(\sliderulewidth/2) * log10(#1)}
\def\cdscalefn(#1){(\sliderulewidth * log10(#1))}
\def\ciscalefn(#1){(\sliderulewidth - \cdscalefn(#1))}
\def\kscalefn(#1){(\sliderulewidth/3) * log10(#1)}
\def\lscalefn(#1){(\sliderulewidth * #1)}
\def\tscalefn(#1){(\sliderulewidth * log10(10 * tan(#1)))}
\def\sscalefn(#1){(\sliderulewidth * log10(10 * sin(#1)))}
% Arguments:
% Label
% x of start
% y of start
\newcommand{\linearscale}[3]{
\draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2);
\draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9);
\draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7);
\draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7);
\draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3};
% Numbers and marks
\foreach \i in {0,..., 10}{
\draw[black]
({#1 + (\sliderulewidth / 10) * \i}, #2) --
({#1 + (\sliderulewidth / 10) * \i}, #2 + 0.3)
node[above] {\i};
}
% Submarks
\foreach \n in {0, ..., 9} {
\foreach \i in {1,..., 9} {
\ifthenelse{\i=5}{
\draw[black]
({#1 + (\sliderulewidth / 10) * (\n + \i / 10)}, #2) --
({#1 + (\sliderulewidth / 10) * (\n + \i / 10)}, #2 + 0.2);
} {
\draw[black]
({#1 + (\sliderulewidth / 10) * (\n + \i / 10)}, #2) --
({#1 + (\sliderulewidth / 10) * (\n + \i / 10)}, #2 + 0.1);
}
}
}
}
% Arguments:
% Label
% x of start
% y of start
\newcommand{\abscale}[3]{
\draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2);
\draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9);
\draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7);
\draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7);
\draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3};
% Numbers and marks 1 - 9
\foreach \i in {1,..., 9}{
\draw[black]
({#1 + \abscalefn(\i)}, #2) --
({#1 + \abscalefn(\i)}, #2 + 0.3)
node[above] {\i};
}
% Numbers and marks 10 - 100
\foreach \i in {1,..., 10}{
\draw[black]
({#1 + \abscalefn(10 * \i)}, #2) --
({#1 + \abscalefn(10 * \i)}, #2 + 0.3)
node[above] {\ifthenelse{\i=10}{1}{\i}};
}
% Submarks 1 - 9
\foreach \n in {1, ..., 9} {
\ifthenelse{\n<5}{
\foreach \i in {1,..., 9}
} {
\foreach \i in {2,4,6,8}
}
{
\ifthenelse{\i=5}{
\draw[black]
({#1 + \abscalefn(\n + \i / 10)}, #2) --
({#1 + \abscalefn(\n + \i / 10)}, #2 + 0.2);
} {
\draw[black]
({#1 + \abscalefn(\n + \i / 10)}, #2) --
({#1 + \abscalefn(\n + \i / 10)}, #2 + 0.1);
}
}
}
% Submarks 10 - 100
\foreach \n in {10,20,...,90} {
\ifthenelse{\n<50}{
\foreach \i in {1,..., 9}
} {
\foreach \i in {2,4,6,8}
}
{
\ifthenelse{\i=5}{
\draw[black]
({#1 + \abscalefn(\n + \i)}, #2) --
({#1 + \abscalefn(\n + \i)}, #2 + 0.2);
} {
\draw[black]
({#1 + \abscalefn(\n + \i)}, #2) --
({#1 + \abscalefn(\n + \i)}, #2 + 0.1);
}
}
}
}
\newcommand{\cdscale}[3]{
\draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2);
\draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9);
\draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7);
\draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7);
\draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3};
% Numbers and marks 1 - 10
\foreach \i in {1,..., 10}{
\draw[black]
({#1 + \cdscalefn(\i)}, #2) --
({#1 + \cdscalefn(\i)}, #2 + 0.3)
node[above] {\ifthenelse{\i=10}{1}{\i}};
}
% Submarks 1 - 9
\foreach \n in {1, ..., 9} {
\ifthenelse{\n<3}{
\foreach \i in {5,10,...,95}
} {
\foreach \i in {10,20,...,90}
}
{
\ifthenelse{\i=50}{
\draw[black]
({#1 + \cdscalefn(\n + \i / 100)}, #2) --
({#1 + \cdscalefn(\n + \i / 100)}, #2 + 0.2);
\ifthenelse{\n=1}{
\draw
({#1 + \cdscalefn(\n + \i / 100)}, #2 + 0.2)
node [above] {1.5};
}{}
} {
\ifthenelse{
\i=10 \OR \i=20 \OR \i=30 \OR \i=40 \OR
\i=60 \OR \i=70 \OR \i=80 \OR \i=90
}{
\draw[black]
({#1 + \cdscalefn(\n + \i / 100)}, #2) --
({#1 + \cdscalefn(\n + \i / 100)}, #2 + 0.15);
} {
\draw[black]
({#1 + \cdscalefn(\n + \i / 100)}, #2) --
({#1 + \cdscalefn(\n + \i / 100)}, #2 + 0.1);
}
}
}
}
}
\newcommand{\ciscale}[3]{
\draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2);
\draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9);
\draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7);
\draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7);
\draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3};
% Numbers and marks
\foreach \i in {1,...,10}{
\draw[black]
({#1 + \ciscalefn(\i)}, #2) --
({#1 + \ciscalefn(\i)}, #2 + 0.3)
node[above] {\ifthenelse{\i=10}{1}{\ifthenelse{\i=0}{0}{.\i}}};
}
% Submarks 1 - 9
\foreach \n in {1, ..., 9} {
\ifthenelse{\n<3}{
\foreach \i in {5,10,...,95}
} {
\foreach \i in {10,20,...,90}
}
{
\ifthenelse{\i=50}{
\draw[black]
({#1 + \ciscalefn(\n + \i / 100)}, #2) --
({#1 + \ciscalefn(\n + \i / 100)}, #2 + 0.2);
\ifthenelse{\n=1}{
\draw
({#1 + \ciscalefn(\n + \i / 100)}, #2 + 0.2)
node [above] {1.5};
}{}
} {
\ifthenelse{
\i=10 \OR \i=20 \OR \i=30 \OR \i=40 \OR
\i=60 \OR \i=70 \OR \i=80 \OR \i=90
}{
\draw[black]
({#1 + \ciscalefn(\n + \i / 100)}, #2) --
({#1 + \ciscalefn(\n + \i / 100)}, #2 + 0.15);
} {
\draw[black]
({#1 + \ciscalefn(\n + \i / 100)}, #2) --
({#1 + \ciscalefn(\n + \i / 100)}, #2 + 0.1);
}
}
}
}
}
\newcommand{\kscale}[3]{
\draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2);
\draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9);
\draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7);
\draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7);
\draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3};
% Numbers and marks 1 - 9
\foreach \i in {1,...,9}{
\draw[black]
({#1 + \kscalefn(\i)}, #2) --
({#1 + \kscalefn(\i)}, #2 + 0.3)
node[above] {\i};
}
% Numbers and marks 10 - 90
\foreach \i in {1,..., 9}{
\draw[black]
({#1 + \kscalefn(10 * \i)}, #2) --
({#1 + \kscalefn(10 * \i)}, #2 + 0.3)
node[above] {\ifthenelse{\i=10}{1}{\i}};
}
% Numbers and marks 100 - 1000
\foreach \i in {1,..., 10}{
\draw[black]
({#1 + \kscalefn(100 * \i)}, #2) --
({#1 + \kscalefn(100 * \i)}, #2 + 0.3)
node[above] {\ifthenelse{\i=10}{1}{\i}};
}
% Submarks 1 - 9
\foreach \n in {1, ..., 9} {
\ifthenelse{\n<4}{
\foreach \i in {1,..., 9}
} {
\foreach \i in {2,4,6,8}
}
{
\ifthenelse{\i=5}{
\draw[black]
({#1 + \kscalefn(\n + \i / 10)}, #2) --
({#1 + \kscalefn(\n + \i / 10)}, #2 + 0.2);
} {
\draw[black]
({#1 + \kscalefn(\n + \i / 10)}, #2) --
({#1 + \kscalefn(\n + \i / 10)}, #2 + 0.1);
}
}
}
% Submarks 10 - 90
\foreach \n in {10,20,...,90} {
\ifthenelse{\n<40}{
\foreach \i in {1,..., 9}
} {
\foreach \i in {2,4,6,8}
}
{
\ifthenelse{\i=5}{
\draw[black]
({#1 + \kscalefn(\n + \i)}, #2) --
({#1 + \kscalefn(\n + \i)}, #2 + 0.2);
} {
\draw[black]
({#1 + \kscalefn(\n + \i)}, #2) --
({#1 + \kscalefn(\n + \i)}, #2 + 0.1);
}
}
}
% Submarks 100 - 1000
\foreach \n in {100,200,...,900} {
\ifthenelse{\n<400}{
\foreach \i in {10,20,...,90}
} {
\foreach \i in {20,40,60,80}
}
{
\ifthenelse{\i=50}{
\draw[black]
({#1 + \kscalefn(\n + \i)}, #2) --
({#1 + \kscalefn(\n + \i)}, #2 + 0.2);
} {
\draw[black]
({#1 + \kscalefn(\n + \i)}, #2) --
({#1 + \kscalefn(\n + \i)}, #2 + 0.1);
}
}
}
}
\newcommand{\lscale}[3]{
\draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2);
\draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9);
\draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7);
\draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7);
\draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3};
% Numbers and marks
\foreach \i in {0,..., 10}{
\draw[black]
({#1 + \lscalefn(\i / 10)}, #2) --
({#1 + \lscalefn(\i / 10)}, #2 + 0.3)
node[above] {\ifthenelse{\i=10}{1}{\ifthenelse{\i=0}{0}{.\i}}};
}
% Submarks
\foreach \n in {0, ..., 9} {
\foreach \i in {1,...,19} {
\ifthenelse{\i=10}{
\draw[black]
({#1 + \lscalefn((\n + (\i / 20))/10)}, #2) --
({#1 + \lscalefn((\n + (\i / 20))/10)}, #2 + 0.2);
} {
\ifthenelse{
\i=1 \OR \i=3 \OR \i=5 \OR \i=7 \OR
\i=9 \OR \i=11 \OR \i=13 \OR \i=15 \OR
\i=17 \OR \i=19
}{
\draw[black]
({#1 + \lscalefn((\n + (\i / 20))/10)}, #2) --
({#1 + \lscalefn((\n + (\i / 20))/10)}, #2 + 0.1);
} {
\draw[black]
({#1 + \lscalefn((\n + (\i / 20))/10)}, #2) --
({#1 + \lscalefn((\n + (\i / 20))/10)}, #2 + 0.15);
}
}
}
}
}
\newcommand{\tscale}[3]{
\draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2);
\draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9);
\draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7);
\draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7);
% First line
\draw[black] ({#1}, #2) -- ({#1}, #2 + 0.2);
\draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3};
% Numbers and marks 6 - 10
\foreach \i in {6,...,9,10,15,...,45}{
\draw[black]
({#1 + \tscalefn(\i)}, #2) --
({#1 + \tscalefn(\i)}, #2 + 0.3)
node[above] {\i};
}
% Submarks 6 - 10
\foreach \n in {6, ..., 9} {
\foreach \i in {1,...,9}{
\ifthenelse{\i=5}{
\draw[black]
({#1 + \tscalefn(\n + \i / 10)}, #2) --
({#1 + \tscalefn(\n + \i / 10)}, #2 + 0.2);
} {
\draw[black]
({#1 + \tscalefn(\n + \i / 10)}, #2) --
({#1 + \tscalefn(\n + \i / 10)}, #2 + 0.1);
}
}
}
% Submarks 15 - 45
\foreach \n in {10, 15, ..., 40} {
\foreach \i in {1,...,24}{
\ifthenelse{
\i=5 \OR \i=10 \OR \i=15 \OR \i=20
} {
\draw[black]
({#1 + \tscalefn(\n + \i / 5)}, #2) --
({#1 + \tscalefn(\n + \i / 5)}, #2 + 0.2);
} {
\draw[black]
({#1 + \tscalefn(\n + \i / 5)}, #2) --
({#1 + \tscalefn(\n + \i / 5)}, #2 + 0.1);
}
}
}
}
\newcommand{\sscale}[3]{
\draw[black] ({#1}, #2) -- ({#1 + \sliderulewidth}, #2);
\draw[black] ({#1}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.9);
\draw[black] ({#1}, #2 + 0.9) -- ({#1}, #2 + 0.7);
\draw[black] ({#1 + \sliderulewidth}, #2 + 0.9) -- ({#1 + \sliderulewidth}, #2 + 0.7);
% First line
\draw[black] ({#1}, #2) -- ({#1}, #2 + 0.2);
\draw ({#1 - 0.1}, #2 + 0.5) node[left] {#3};
% Numbers and marks
\foreach \i in {6,...,9,10,15,...,30,40,50,...,60,90}{
\draw[black]
({#1 + \sscalefn(\i)}, #2) --
({#1 + \sscalefn(\i)}, #2 + 0.3)
node[above] {\i};
}
% Submarks 6 - 10
\foreach \n in {6, ..., 9} {
\foreach \i in {1,...,9}{
\ifthenelse{\i=5}{
\draw[black]
({#1 + \sscalefn(\n + \i / 10)}, #2) --
({#1 + \sscalefn(\n + \i / 10)}, #2 + 0.2);
} {
\draw[black]
({#1 + \sscalefn(\n + \i / 10)}, #2) --
({#1 + \sscalefn(\n + \i / 10)}, #2 + 0.1);
}
}
}
% Submarks 15 - 30
\foreach \n in {10, 15, ..., 25} {
\foreach \i in {1,...,24}{
\ifthenelse{
\i=5 \OR \i=10 \OR \i=15 \OR \i=20
} {
\draw[black]
({#1 + \sscalefn(\n + \i / 5)}, #2) --
({#1 + \sscalefn(\n + \i / 5)}, #2 + 0.2);
} {
\draw[black]
({#1 + \sscalefn(\n + \i / 5)}, #2) --
({#1 + \sscalefn(\n + \i / 5)}, #2 + 0.1);
}
}
}
% Submarks 30
\foreach \n in {30} {
\foreach \i in {1,...,19}{
\ifthenelse{
\i=2 \OR \i=4 \OR \i=6 \OR \i=8 \OR
\i=10 \OR \i=12 \OR \i=14 \OR \i=16 \OR
\i=18
} {
\draw[black]
({#1 + \sscalefn(\n + \i / 2)}, #2) --
({#1 + \sscalefn(\n + \i / 2)}, #2 + 0.2);
} {
\draw[black]
({#1 + \sscalefn(\n + \i / 2)}, #2) --
({#1 + \sscalefn(\n + \i / 2)}, #2 + 0.1);
}
}
}
% Submarks 40 - 50
\foreach \n in {40, 50} {
\foreach \i in {1,...,9}{
\ifthenelse{
\i=5 \OR \i=10 \OR \i=15 \OR \i=20
} {
\draw[black]
({#1 + \sscalefn(\n + \i)}, #2) --
({#1 + \sscalefn(\n + \i)}, #2 + 0.2);
} {
\draw[black]
({#1 + \sscalefn(\n + \i)}, #2) --
({#1 + \sscalefn(\n + \i)}, #2 + 0.1);
}
}
}
% Submarks 60
\foreach \i in {1,...,10}{
\ifthenelse{
\i=5 \OR \i=10
} {
\draw[black]
({#1 + \sscalefn(60 + \i * 2)}, #2) --
({#1 + \sscalefn(60 + \i * 2)}, #2 + 0.2);
} {
\draw[black]
({#1 + \sscalefn(60 + \i * 2)}, #2) --
({#1 + \sscalefn(60 + \i * 2)}, #2 + 0.1);
}
}
}