Add Advanced/Fast Inverse Root
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -3,6 +3,7 @@ venv | ||||
| __pycache__ | ||||
| *.ignore | ||||
| .mypy_cache | ||||
| .DS_Store | ||||
|  | ||||
| # Output files | ||||
| /output | ||||
| @ -10,7 +11,7 @@ __pycache__ | ||||
| *.pdf | ||||
|  | ||||
| # TeX build files | ||||
| *.synctex.gz* | ||||
| *.synctex* | ||||
| *.latexmk | ||||
| *.aux | ||||
| *.out | ||||
|  | ||||
| @ -1,18 +1,3 @@ | ||||
| % Copyright (C) 2023 Mark (mark@betalupi.com) | ||||
| % | ||||
| % This program is free software: you can redistribute it and/or modify | ||||
| % it under the terms of the GNU General Public License as published by | ||||
| % the Free Software Foundation, either version 3 of the License, or | ||||
| % (at your option) any later version. | ||||
| % | ||||
| % This program is distributed in the hope that it will be useful, | ||||
| % but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| % GNU General Public License for more details. | ||||
| % | ||||
| % You should have received a copy of the GNU General Public License | ||||
| % along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
|  | ||||
| \NeedsTeXFormat{LaTeX2e} | ||||
| \ProvidesClass{../../../lib/tex/ormc_handout}[2023/05/29 2.0.2 ORMC Handout] | ||||
|  | ||||
|  | ||||
| @ -44,6 +44,14 @@ | ||||
|     by = text(size: 10pt, [Prepared by #by on #date]) | ||||
|   } | ||||
|  | ||||
|   let sub = () | ||||
|   if (by != none) { | ||||
|     sub.push(by) | ||||
|   } | ||||
|   if (subtitle != none) { | ||||
|     sub.push(subtitle) | ||||
|   } | ||||
|  | ||||
|   // Main title | ||||
|   align( | ||||
|     center, | ||||
| @ -61,8 +69,7 @@ | ||||
|           // Title | ||||
|           text(size: 20pt, title), | ||||
|           // Subtitle | ||||
|           if (by != none) { text(size: 10pt, by) }, | ||||
|           if (subtitle != none) { text(size: 10pt, subtitle) }, | ||||
|           ..sub, | ||||
|           line(length: 100%, stroke: 0.2mm), | ||||
|         ), | ||||
|       ), | ||||
|  | ||||
| @ -3,8 +3,14 @@ | ||||
| // Re-exports | ||||
| // All functions that maybe used by client code are listed here | ||||
| #import "misc.typ": * | ||||
| #import "object.typ": problem, definition, theorem | ||||
| #import "solution.typ": if_solutions, if_no_solutions, solution | ||||
| #import "object.typ": problem, definition, theorem, example, remark, generic | ||||
| #import "solution.typ": ( | ||||
|   if_solutions, | ||||
|   if_no_solutions, | ||||
|   if_solutions_else, | ||||
|   solution, | ||||
|   instructornote, | ||||
| ) | ||||
|  | ||||
|  | ||||
| /// Main handout wrapper. | ||||
|  | ||||
| @ -98,3 +98,11 @@ | ||||
| #let theorem = _mkobj("Theorem") | ||||
| #let example = _mkobj("Example") | ||||
| #let remark = _mkobj("Remark") | ||||
|  | ||||
| #let generic(obj_content) = { | ||||
|   block( | ||||
|     above: 8mm, | ||||
|     below: 2mm, | ||||
|     text(weight: "bold", obj_content), | ||||
|   ) | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| #import "misc.typ": ored | ||||
| #import "misc.typ": ored, oblue | ||||
|  | ||||
| /// If false, hide instructor info. | ||||
| /// | ||||
| @ -27,6 +27,10 @@ | ||||
|   if not show_solutions { content } | ||||
| } | ||||
|  | ||||
| #let if_solutions_else(if_yes, if_no) = { | ||||
|   if show_solutions { if_yes } else { if_no } | ||||
| } | ||||
|  | ||||
| #let solution(content) = { | ||||
|   if_solutions( | ||||
|     align( | ||||
| @ -54,3 +58,31 @@ | ||||
|     ), | ||||
|   ) | ||||
| } | ||||
|  | ||||
| #let instructornote(content) = { | ||||
|   if_solutions( | ||||
|     align( | ||||
|       center, | ||||
|       stack( | ||||
|         block( | ||||
|           width: 100%, | ||||
|           breakable: false, | ||||
|           fill: oblue, | ||||
|           stroke: oblue + 2pt, | ||||
|           inset: 1.5mm, | ||||
|           align(left, text(fill: white, weight: "bold", [Instructor note:])), | ||||
|         ), | ||||
|  | ||||
|         block( | ||||
|           width: 100%, | ||||
|           height: auto, | ||||
|           breakable: false, | ||||
|           fill: oblue.lighten(80%).desaturate(10%), | ||||
|           stroke: oblue + 2pt, | ||||
|           inset: 3mm, | ||||
|           align(left, content), | ||||
|         ), | ||||
|       ), | ||||
|     ), | ||||
|   ) | ||||
| } | ||||
|  | ||||
							
								
								
									
										31
									
								
								src/Advanced/Fast Inverse Root/main.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/Advanced/Fast Inverse Root/main.typ
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| #import "@local/handout:0.1.0": * | ||||
|  | ||||
| // Bonus: | ||||
| // - Floats vs fixed point | ||||
| // - Float density | ||||
| // - Find non-floatable rational numbers | ||||
| // - What if we use `n`-bit floats? | ||||
|  | ||||
| #show: doc => handout( | ||||
|   doc, | ||||
|   group: "Advanced 2", | ||||
|   title: [Fast Inverse Square Root], | ||||
|   by: "Mark", | ||||
| ) | ||||
|  | ||||
| #include "parts/00 intro.typ" | ||||
| #pagebreak() | ||||
|  | ||||
| #include "parts/01 int.typ" | ||||
| #pagebreak() | ||||
|  | ||||
| #include "parts/02 float.typ" | ||||
| #pagebreak() | ||||
|  | ||||
| #include "parts/03 approx.typ" | ||||
| #pagebreak() | ||||
|  | ||||
| #include "parts/04 quake.typ" | ||||
| #pagebreak() | ||||
|  | ||||
| #include "parts/05 bonus.typ" | ||||
							
								
								
									
										7
									
								
								src/Advanced/Fast Inverse Root/meta.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/Advanced/Fast Inverse Root/meta.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| [metadata] | ||||
| title = "Fast Inverse Square Root" | ||||
|  | ||||
|  | ||||
| [publish] | ||||
| handout = true | ||||
| solutions = true | ||||
							
								
								
									
										45
									
								
								src/Advanced/Fast Inverse Root/parts/00 intro.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/Advanced/Fast Inverse Root/parts/00 intro.typ
									
									
									
									
									
										Normal 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. | ||||
							
								
								
									
										102
									
								
								src/Advanced/Fast Inverse Root/parts/01 int.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/Advanced/Fast Inverse Root/parts/01 int.typ
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | ||||
| #import "@local/handout:0.1.0": * | ||||
|  | ||||
| = Integers | ||||
|  | ||||
| #definition() | ||||
| A _bit string_ is a string of binary digits. \ | ||||
| In this handout, we'll denote bit strings with the prefix `0b`. \ | ||||
| #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. \ | ||||
| Underscores have no meaning: $#text([`0b1111_0000`]) = #text([`0b11110000`])$. | ||||
|  | ||||
| #problem() | ||||
| What is the value of the following bit strings, if we interpret them as integers in base 2? | ||||
| - `0b0001_1010` | ||||
| - `0b0110_0001` | ||||
|  | ||||
| #solution([ | ||||
|   - $#text([`0b0001_1010`]) = 2 + 8 + 16 = 26$ | ||||
|   - $#text([`0b0110_0001`]) = 1 + 32 + 64 = 95$ | ||||
| ]) | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
| #definition() | ||||
| We can interpret a bit string in any number of ways. \ | ||||
| One such interpretation is the _unsigned integer_, or `uint` for short. \ | ||||
| `uint`s allow us to represent positive (hence "unsigned") integers using 32-bit strings. | ||||
|  | ||||
| #v(2mm) | ||||
|  | ||||
| The value of a `uint` is simply its value as a binary number: | ||||
| - $#text([`0b00000000_00000000_00000000_00000000`]) = 0$ | ||||
| - $#text([`0b00000000_00000000_00000000_00000011`]) = 3$ | ||||
| - $#text([`0b00000000_00000000_00000000_00100000`]) = 32$ | ||||
| - $#text([`0b00000000_00000000_00000000_10000010`]) = 130$ | ||||
|  | ||||
| #problem() | ||||
| What is the largest number we can represent with a 32-bit `uint`? | ||||
|  | ||||
| #solution([ | ||||
|   $#text([`0b11111111_11111111_11111111_11111111`]) = 2^(32)-1$ | ||||
| ]) | ||||
|  | ||||
| #v(1fr) | ||||
| #pagebreak() | ||||
|  | ||||
| #problem() | ||||
| Find the value of each of the following 32-bit unsigned integers: | ||||
| - `0b00000000_00000000_00000101_00111001` | ||||
| - `0b00000000_00000000_00000001_00101100` | ||||
| - `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$ | ||||
|   - $#text([`0b00000000_00000000_00000010_01011000`]) = 1200$ | ||||
|   Notice that the third int is the second shifted left twice (i.e, multiplied by 4) | ||||
| ]) | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
|  | ||||
| #definition() | ||||
| 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 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 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: \ | ||||
| $#text[`00110`] #text[`>>`] n$ means "shift `0b0110` right $n$ times." \ | ||||
| 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$ | ||||
|   - $27 #text[`>>`] 3 = 3$ | ||||
|   - $16 #text[`>>`] 8 = 0$ | ||||
| ] | ||||
|  | ||||
| #v(1fr) | ||||
							
								
								
									
										207
									
								
								src/Advanced/Fast Inverse Root/parts/02 float.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								src/Advanced/Fast Inverse Root/parts/02 float.typ
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,207 @@ | ||||
| #import "@local/handout:0.1.0": * | ||||
| #import "@preview/cetz:0.3.1" | ||||
|  | ||||
| = Floats | ||||
| #definition() | ||||
| _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)$ | ||||
| - $0.0208 = 2 times 10^(-2) + 8 times 10^(-4)$ | ||||
|  | ||||
| #v(5mm) | ||||
|  | ||||
| We can do the same in base 2: | ||||
| - $#text([`0.1`]) = 2^(-1) = 0.5$ | ||||
| - $#text([`0.011`]) = 2^(-2) + 2^(-3) = 0.375$ | ||||
| - $#text([`101.01`]) = 5.125$ | ||||
|  | ||||
| #v(5mm) | ||||
|  | ||||
| #problem() | ||||
| Rewrite the following binary decimals in base 10: \ | ||||
| #note([You may leave your answer as a fraction.]) | ||||
| - `1011.101` | ||||
| - `110.1101` | ||||
|  | ||||
|  | ||||
| #v(1fr) | ||||
| #pagebreak() | ||||
|  | ||||
| #definition() | ||||
| Another way we can interpret a bit string is as a _signed floating-point decimal_, or a `float` for short. \ | ||||
| Floats represent a subset of the real numbers, and are interpreted as follows: \ | ||||
| #note([The following only applies to floats that consist of 32 bits. We won't encounter any others today.]) | ||||
|  | ||||
| #align( | ||||
|   center, | ||||
|   box( | ||||
|     inset: 2mm, | ||||
|     cetz.canvas({ | ||||
|       import cetz.draw: * | ||||
|  | ||||
|       let chars = ( | ||||
|         `0`, | ||||
|         `b`, | ||||
|         `0`, | ||||
|         `_`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `_`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `_`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `_`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|         `0`, | ||||
|       ) | ||||
|  | ||||
|       let x = 0 | ||||
|       for c in chars { | ||||
|         content((x, 0), c) | ||||
|         x += 0.25 | ||||
|       } | ||||
|  | ||||
|       let y = -0.4 | ||||
|       line((0.3, y), (0.65, y)) | ||||
|       content((0.45, y - 0.2), [s]) | ||||
|  | ||||
|       line((0.85, y), (2.9, y)) | ||||
|       content((1.9, y - 0.2), [exponent]) | ||||
|  | ||||
|       line((3.10, y), (9.4, y)) | ||||
|       content((6.3, y - 0.2), [fraction]) | ||||
|     }), | ||||
|   ), | ||||
| ) | ||||
|  | ||||
| - The first bit denotes the sign of the float's value | ||||
|   We'll label it $s$. \ | ||||
|   If $s = #text([`1`])$, this float is negative; if $s = #text([`0`])$, it is positive. | ||||
|  | ||||
| - 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)]) | ||||
|  | ||||
| - 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") | ||||
| Consider `0b01000001_10101000_00000000_00000000`. \ | ||||
| Find the $s$, $E$, and $F$ we get if we interpret this bit string as a `float`. \ | ||||
| #note([Leave $F$ as a sum of powers of two.]) | ||||
|  | ||||
| #solution([ | ||||
|   $s = 0$ \ | ||||
|   $E = 258$ \ | ||||
|   $F = 2^31+2^19 = 2,621,440$ | ||||
| ]) | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
|  | ||||
| #definition(label: "floatdef") | ||||
| The final value of a float with sign $s$, exponent $E$, and fraction $F$ is | ||||
|  | ||||
| $ | ||||
|   (-1)^s times 2^(E - 127) times (1 + F / (2^(23))) | ||||
| $ | ||||
|  | ||||
| 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. \ | ||||
|  | ||||
| #v(2mm) | ||||
|  | ||||
| What value do we get if we interpret this bit string as a float? \ | ||||
| #hint([$21 div 16 = 1.3125$]) | ||||
|  | ||||
| #solution([ | ||||
|   This is 21: | ||||
|   $ | ||||
|     2^(131) times (1 + (2^(21) + 2^(19)) / (2^(23))) | ||||
|     = 2^(4) times (1 + 0.25 + 0.0625) | ||||
|     = 16 times (1.3125) | ||||
|     = 21 | ||||
|   $ | ||||
| ]) | ||||
|  | ||||
| #v(1fr) | ||||
| #pagebreak() | ||||
|  | ||||
| #problem() | ||||
| Encode $12.5$ as a float. \ | ||||
| #hint([$12.5 div 8 = 1.5625$]) | ||||
|  | ||||
| #solution([ | ||||
|   $ | ||||
|     12.5 | ||||
|     = 8 times 1.5625 | ||||
|     = 2^(3) times (1 + (0.5 + 0.0625)) | ||||
|     = 2^(130) times (1 + (2^(22) + 2^(19)) / (2^(23))) | ||||
|   $ | ||||
|  | ||||
|   which is `0b01000001_01001000_00000000_00000000`. \ | ||||
| ]) | ||||
|  | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
| #definition() | ||||
| Say we have a bit string $x$. \ | ||||
| We'll let $x_f$ denote the value we get if we interpret $x$ as a float, \ | ||||
| and we'll let $x_i$ denote the value we get if we interpret $x$ an integer. | ||||
|  | ||||
| #problem() | ||||
| Let $x = #text[`0b01000001_01001000_00000000_00000000`]$. \ | ||||
| What are $x_f$ and $x_i$? #note([As always, you may leave big numbers as powers of two.]) | ||||
| #solution([ | ||||
|   $x_f = 12.5$ | ||||
|  | ||||
|   #v(2mm) | ||||
|  | ||||
|   $x_i = 2^30 + 2^24 + 2^22 + 2^19 = 11,095,237,632$ | ||||
| ]) | ||||
|  | ||||
| #v(1fr) | ||||
							
								
								
									
										173
									
								
								src/Advanced/Fast Inverse Root/parts/03 approx.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/Advanced/Fast Inverse Root/parts/03 approx.typ
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,173 @@ | ||||
| #import "@local/handout:0.1.0": * | ||||
| #import "@preview/cetz:0.3.1" | ||||
| #import "@preview/cetz-plot:0.1.0": plot, chart | ||||
|  | ||||
| = Integers and Floats | ||||
|  | ||||
| #generic("Observation:") | ||||
| 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) | ||||
|  | ||||
| We'll add the _correction term_ $epsilon$ to our approximation: $log_2(1 + a) approx a + epsilon$. \ | ||||
| This allows us to improve the average error of our linear approximation: | ||||
|  | ||||
| #table( | ||||
|   stroke: none, | ||||
|   align: center, | ||||
|   columns: (1fr, 1fr), | ||||
|   inset: 5mm, | ||||
|   [$log_2(1+x)$ and $x + 0$] | ||||
|     + cetz.canvas({ | ||||
|       import cetz.draw: * | ||||
|  | ||||
|       let f1(x) = calc.log(calc.abs(x + 1), base: 2) | ||||
|       let f2(x) = x | ||||
|  | ||||
|       // Set-up a thin axis style | ||||
|       set-style(axes: (stroke: .5pt, tick: (stroke: .5pt))) | ||||
|  | ||||
|  | ||||
|       plot.plot( | ||||
|         size: (7, 7), | ||||
|         x-tick-step: 0.2, | ||||
|         y-tick-step: 0.2, | ||||
|         y-min: 0, | ||||
|         y-max: 1, | ||||
|         x-min: 0, | ||||
|         x-max: 1, | ||||
|         legend: none, | ||||
|         axis-style: "scientific-auto", | ||||
|         x-label: none, | ||||
|         y-label: none, | ||||
|         { | ||||
|           let domain = (0, 1) | ||||
|  | ||||
|           plot.add( | ||||
|             f1, | ||||
|             domain: domain, | ||||
|             label: $log(1+x)$, | ||||
|             style: (stroke: ogrape), | ||||
|           ) | ||||
|  | ||||
|           plot.add( | ||||
|             f2, | ||||
|             domain: domain, | ||||
|             label: $x$, | ||||
|             style: (stroke: oblue), | ||||
|           ) | ||||
|         }, | ||||
|       ) | ||||
|     }) | ||||
|     + [ | ||||
|       Max error: 0.086 \ | ||||
|       Average error: 0.0573 | ||||
|     ], | ||||
|   [$log_2(1+x)$ and $x + 0.045$] | ||||
|     + cetz.canvas({ | ||||
|       import cetz.draw: * | ||||
|  | ||||
|       let f1(x) = calc.log(calc.abs(x + 1), base: 2) | ||||
|       let f2(x) = x + 0.0450466 | ||||
|  | ||||
|       // Set-up a thin axis style | ||||
|       set-style(axes: (stroke: .5pt, tick: (stroke: .5pt))) | ||||
|  | ||||
|  | ||||
|       plot.plot( | ||||
|         size: (7, 7), | ||||
|         x-tick-step: 0.2, | ||||
|         y-tick-step: 0.2, | ||||
|         y-min: 0, | ||||
|         y-max: 1, | ||||
|         x-min: 0, | ||||
|         x-max: 1, | ||||
|         legend: none, | ||||
|         axis-style: "scientific-auto", | ||||
|         x-label: none, | ||||
|         y-label: none, | ||||
|         { | ||||
|           let domain = (0, 1) | ||||
|  | ||||
|           plot.add( | ||||
|             f1, | ||||
|             domain: domain, | ||||
|             label: $log(1+x)$, | ||||
|             style: (stroke: ogrape), | ||||
|           ) | ||||
|  | ||||
|           plot.add( | ||||
|             f2, | ||||
|             domain: domain, | ||||
|             label: $x$, | ||||
|             style: (stroke: oblue), | ||||
|           ) | ||||
|         }, | ||||
|       ) | ||||
|     }) | ||||
|     + [ | ||||
|       Max error: 0.041 \ | ||||
|       Average error: 0.0254 | ||||
|     ], | ||||
| ) | ||||
|  | ||||
|  | ||||
| A suitiable value of $epsilon$ can be found using calculus or with computational trial-and-error. \ | ||||
| We won't bother with this---we'll simply leave the correction term as an opaque constant $epsilon$. | ||||
|  | ||||
|  | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
| #note( | ||||
|   type: "Note", | ||||
|   [ | ||||
|     "Average error" above is simply the area of the region between the two graphs: | ||||
|     $ | ||||
|       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. | ||||
|   ], | ||||
| ) | ||||
|  | ||||
|  | ||||
| #pagebreak() | ||||
|  | ||||
| #problem(label: "convert") | ||||
| Use the fact that $log_2(1 + a) approx a + epsilon$ to approximate $log_2(x_f)$ in terms of $x_i$. \ | ||||
| Namely, show that | ||||
| $ | ||||
|   log_2(x_f) = (x_i) / (2^23) - 127 + epsilon | ||||
| $ | ||||
| #note([ | ||||
|   In other words, we're finding an expression for $x$ as a float | ||||
|   in terms of $x$ as an int. | ||||
| ]) | ||||
|  | ||||
| #solution([ | ||||
|   Let $E$ and $F$ be the exponent and float bits of $x_f$. \ | ||||
|   We then have: | ||||
|   $ | ||||
|     log_2(x_f) | ||||
|     &= log_2 ( 2^(E-127) times (1 + (F) / (2^23)) ) \ | ||||
|     &= E - 127 + log_2(1 + F / (2^23)) \ | ||||
|     & approx E-127 + F / (2^23) + epsilon \ | ||||
|     &= 1 / (2^23)(2^23 E + F) - 127 + epsilon \ | ||||
|     &= 1 / (2^23)(x_i) - 127 + epsilon | ||||
|   $ | ||||
| ]) | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
|  | ||||
| #problem() | ||||
| Using basic log rules, rewrite $log_2(1 / sqrt(x))$ in terms of $log_2(x)$. | ||||
|  | ||||
| #solution([ | ||||
|   $ | ||||
|     log_2(1 / sqrt(x)) = (-1) / (2)log_2(x) | ||||
|   $ | ||||
| ]) | ||||
|  | ||||
| #v(1fr) | ||||
							
								
								
									
										210
									
								
								src/Advanced/Fast Inverse Root/parts/04 quake.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								src/Advanced/Fast Inverse Root/parts/04 quake.typ
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,210 @@ | ||||
| #import "@local/handout:0.1.0": * | ||||
|  | ||||
| = The Fast Inverse Square Root | ||||
|  | ||||
| A simplified version of the _Quake_ routine we are studying is reproduced below. | ||||
|  | ||||
| #v(2mm) | ||||
|  | ||||
| ```c | ||||
| float Q_rsqrt( float number ) { | ||||
|     long i = * ( long * ) &number; | ||||
|     i = 0x5f3759df - ( i >> 1 ); | ||||
|     return * ( float * ) &i; | ||||
| } | ||||
| ``` | ||||
|  | ||||
| #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) = | ||||
|   6240089 - (n_i div 2) | ||||
|   #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`. | ||||
| ] | ||||
|  | ||||
| #v(2mm) | ||||
|  | ||||
| Our goal in this section is to understand why this works: | ||||
| - How does Quake approximate $1 / sqrt(x)$ by simply subtracting and dividing by two? | ||||
| - What's special about $6240089$? | ||||
|  | ||||
|  | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
| #remark() | ||||
| For those that are interested, here are the details of the "code-to-math" translation: | ||||
|  | ||||
| - "`long i = * (long *) &number`" is C magic that tells the compiler \ | ||||
|   to set `i` to the `uint` value of the bits of `number`. \ | ||||
|   #note[ | ||||
|     "long" refers to a "long integer", which has 32 bits. \ | ||||
|     Normal `int`s have 16 bits, `short int`s have 8. | ||||
|   ] \ | ||||
|   In other words, `number` is $n_f$ and `i` is $n_i$. | ||||
| #v(2mm) | ||||
|  | ||||
|  | ||||
| - Notice the right-shift in the second line of the function. \ | ||||
|   We translated `(i >> 1)` into $(n_i div 2)$. | ||||
| #v(2mm) | ||||
|  | ||||
| - "`return * (float *) &i`" is again C magic. \ | ||||
|   Much like before, it tells us to return the value of the bits of `i` as a float. | ||||
| #pagebreak() | ||||
|  | ||||
| #generic("Setup:") | ||||
| 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. \ | ||||
|  | ||||
| #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 + x)$.] | ||||
|  | ||||
| #solution[ | ||||
|   $ | ||||
|     log_2(r_f) | ||||
|     = log_2(1 / sqrt(n_f)) | ||||
|     = (-1) / 2 log_2(n_f) | ||||
|     approx (-1) / 2 ( (n_i) / (2^23) + epsilon - 127 ) | ||||
|   $ | ||||
| ] | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
| #problem(label: "finalb") | ||||
| 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$ \ | ||||
| #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 | ||||
|   $ | ||||
|     log_2(r_f) approx (r_i) / (2^23) + epsilon - 127 | ||||
|   $ | ||||
|  | ||||
|   Combining this with the result from @finala, we get: | ||||
|   $ | ||||
|     (r_i) / (2^23) + epsilon - 127 | ||||
|     &approx (-1) / (2) ( (n_i) / (2^23) + epsilon - 127) \ | ||||
|     (r_i) / (2^23) | ||||
|     &approx (-1) / (2) ( (n_i) / (2^23)) + 3 / 2 (127 - epsilon) \ | ||||
|     r_i | ||||
|     &approx (-1) / 2 (n_i) + 2^23 3 / 2(127 - epsilon) | ||||
|     = 2^23 3 / 2 (127 - epsilon) - (n_i) / 2 | ||||
|   $ | ||||
|  | ||||
|   #v(2mm) | ||||
|  | ||||
|   This is exactly what we need! If we set $kappa$ to $(3 times 2^22) (127-epsilon)$, then | ||||
|   $ | ||||
|     r_i approx kappa - (n_i div 2) = #text[`Q_sqrt`] (n_f) | ||||
|   $ | ||||
| ] | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
| #problem(label: "finalc") | ||||
| What is the exact value of $kappa$ in terms of $epsilon$? \ | ||||
| #hint[Look at @finalb. We already found it!] | ||||
|  | ||||
| #solution[ | ||||
|   This problem makes sure our students see that | ||||
|   $kappa = (3 times 2^22) (127 - epsilon)$. \ | ||||
|   See the solution to @finalb. | ||||
| ] | ||||
|  | ||||
| #v(2cm) | ||||
|  | ||||
| #pagebreak() | ||||
|  | ||||
| #remark() | ||||
| In @finalc we saw that $kappa = (3 times 2^22) (127 - epsilon)$. \ | ||||
| Looking at the code again, we see that $kappa = #text[`0x5f3759df`]$ in _Quake_: | ||||
|  | ||||
| #v(2mm) | ||||
|  | ||||
| ```c | ||||
| float Q_rsqrt( float number ) { | ||||
|     long i = * ( long * ) &number; | ||||
|     i = 0x5f3759df - ( i >> 1 ); | ||||
|     return * ( float * ) &i; | ||||
| } | ||||
| ``` | ||||
|  | ||||
| #v(2mm) | ||||
| Using a calculator and some basic algebra, we can find the $epsilon$ this code uses: \ | ||||
| #note[Remember, #text[`0x5f3759df`] is $6240089$ in hexadecimal.] | ||||
| $ | ||||
|   (3 times 2^22) (127 - epsilon) &= 6240089 \ | ||||
|   (127 - epsilon) &= 126.955 \ | ||||
|   epsilon &= 0.0450466 | ||||
| $ | ||||
|  | ||||
| So, $0.045$ is the $epsilon$ used by Quake. \ | ||||
| Online sources state that this constant was generated by trial-and-error, \ | ||||
| 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. \ | ||||
|  | ||||
| #v(2mm) | ||||
|  | ||||
| Notably, `Q_sqrt` uses _zero_ divisions or multiplications (`>>` doesn't count). \ | ||||
| This makes it _very_ fast when compared to more traditional approximation techniques (i.e, Taylor series). | ||||
|  | ||||
| #v(2mm) | ||||
|  | ||||
| In the case of _Quake_, this is very important. 3D graphics require thousands of inverse-square-root calculations to render a single frame#footnote[e.g, to generate normal vectors], which is not an easy task for a Playstation running at 300MHz. | ||||
|  | ||||
| #instructornote[ | ||||
|   Let $x$ be a bit string. If we assume $x_f$ is positive and $E$ is even, then | ||||
|   $ | ||||
|     (x #text[`>>`] 1)_f = 2^((E div 2) - 127) times (1 + (F div 2) / (2^(23))) | ||||
|   $ | ||||
|   Notably: a right-shift divides the exponent of $x_f$ by two, \ | ||||
|   which is, of course, a square root! | ||||
|  | ||||
|   #v(2mm) | ||||
|  | ||||
|   This intuition is hand-wavy, though: \ | ||||
|   If $E$ is odd, its lowest-order bit becomes the highest-order bit of $F$ when we shift $x$ right. \ | ||||
|   Also, a right shift doesn't divide the _entire_ exponent, skipping the $-127$ offset. \ | ||||
|  | ||||
|   #v(2mm) | ||||
|  | ||||
|   Remarkably, this intuition is still somewhat correct. \ | ||||
|   The bits align _just so_, and our approximation still works. | ||||
|  | ||||
|   #v(8mm) | ||||
|  | ||||
|   One can think of the fast inverse root as a "digital slide rule": \ | ||||
|   The integer representation of $x_f$ already contains $log_2(x_f)$, offset and scaled. \ | ||||
|   By subtracting and dividing in "log space", we effectively invert and root $x_f$! | ||||
|  | ||||
|   After all, | ||||
|   $ | ||||
|     - 1 / 2 log_2(n_f) = 1 / sqrt(n_f) | ||||
|   $ | ||||
| ] | ||||
							
								
								
									
										36
									
								
								src/Advanced/Fast Inverse Root/parts/05 bonus.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/Advanced/Fast Inverse Root/parts/05 bonus.typ
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| #import "@local/handout:0.1.0": * | ||||
|  | ||||
| = Bonus -- More about Floats | ||||
|  | ||||
| #problem() | ||||
| Convince yourself that all numbers that can be represented as a float are rational. | ||||
|  | ||||
| #problem() | ||||
| Find a rational number that cannot be represented as a float. | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
| #problem() | ||||
| What is the smallest positive 32-bit float? | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
| #problem() | ||||
| What is the largest positive 32-bit float? | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
| #problem() | ||||
| How many floats are between $-1$ and $1$? | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
| #problem() | ||||
| How many floats are between $1$ and $2$? | ||||
|  | ||||
| #v(1fr) | ||||
|  | ||||
| #problem() | ||||
| How many floats are between $1$ and $128$? | ||||
|  | ||||
| #v(1fr) | ||||
| @ -1,18 +1,3 @@ | ||||
| % Copyright (C) 2023 <Mark (mark@betalupi.com)> | ||||
| % | ||||
| % This program is free software: you can redistribute it and/or modify | ||||
| % it under the terms of the GNU General Public License as published by | ||||
| % the Free Software Foundation, either version 3 of the License, or | ||||
| % (at your option) any later version. | ||||
| % | ||||
| % You may have received a copy of the GNU General Public License | ||||
| % along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| % | ||||
| % | ||||
| % | ||||
| % If you edit this, please give credit! | ||||
| % Quality handouts take time to make. | ||||
|  | ||||
| % use the [nosolutions] flag to hide solutions, | ||||
| % use the [solutions] flag to show solutions. | ||||
| \documentclass[ | ||||
|  | ||||
| @ -1,19 +1,3 @@ | ||||
| % Copyright (C) 2023 <Mark (mark@betalupi.com)> | ||||
| % | ||||
| % This program is free software: you can redistribute it and/or modify | ||||
| % it under the terms of the GNU General Public License as published by | ||||
| % the Free Software Foundation, either version 3 of the License, or | ||||
| % (at your option) any later version. | ||||
| % | ||||
| % You may have received a copy of the GNU General Public License | ||||
| % along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| % | ||||
| % | ||||
| % | ||||
| % If you edit this, please give credit! | ||||
| % Quality handouts take time to make. | ||||
|  | ||||
|  | ||||
| % use [nosolutions] flag to hide solutions. | ||||
| % use [solutions] flag to show solutions. | ||||
| \documentclass[ | ||||
|  | ||||
| @ -1,19 +1,3 @@ | ||||
| % Copyright (C) 2023 <Mark (mark@betalupi.com)> | ||||
| % | ||||
| % This program is free software: you can redistribute it and/or modify | ||||
| % it under the terms of the GNU General Public License as published by | ||||
| % the Free Software Foundation, either version 3 of the License, or | ||||
| % (at your option) any later version. | ||||
| % | ||||
| % You may have received a copy of the GNU General Public License | ||||
| % along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| % | ||||
| % | ||||
| % | ||||
| % If you edit this, please give credit! | ||||
| % Quality handouts take time to make. | ||||
|  | ||||
|  | ||||
| \section{Dual Numbers} | ||||
|  | ||||
| \definition{} | ||||
|  | ||||
| @ -1,18 +1,3 @@ | ||||
| % Copyright (C) 2023 <Mark (mark@betalupi.com)> | ||||
| % | ||||
| % This program is free software: you can redistribute it and/or modify | ||||
| % it under the terms of the GNU General Public License as published by | ||||
| % the Free Software Foundation, either version 3 of the License, or | ||||
| % (at your option) any later version. | ||||
| % | ||||
| % You may have received a copy of the GNU General Public License | ||||
| % along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| % | ||||
| % | ||||
| % | ||||
| % If you edit this, please give credit! | ||||
| % Quality handouts take time to make. | ||||
|  | ||||
| \section{Extensions of $\mathbb{R}$} | ||||
|  | ||||
| \definition{} | ||||
|  | ||||
| @ -1,19 +1,3 @@ | ||||
| % Copyright (C) 2023 <Mark (mark@betalupi.com)> | ||||
| % | ||||
| % This program is free software: you can redistribute it and/or modify | ||||
| % it under the terms of the GNU General Public License as published by | ||||
| % the Free Software Foundation, either version 3 of the License, or | ||||
| % (at your option) any later version. | ||||
| % | ||||
| % You may have received a copy of the GNU General Public License | ||||
| % along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| % | ||||
| % | ||||
| % | ||||
| % If you edit this, please give credit! | ||||
| % Quality handouts take time to make. | ||||
|  | ||||
|  | ||||
| \section*{The supremum \& the infimum} | ||||
|  | ||||
| \definition{} | ||||
|  | ||||
							
								
								
									
										78
									
								
								src/Warm-Ups/Slide Rules/main.tex
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										78
									
								
								src/Warm-Ups/Slide Rules/main.tex
									
									
									
									
									
										Executable 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} | ||||
							
								
								
									
										6
									
								
								src/Warm-Ups/Slide Rules/meta.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/Warm-Ups/Slide Rules/meta.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| [metadata] | ||||
| title = "Slide Rules" | ||||
|  | ||||
| [publish] | ||||
| handout = false | ||||
| solutions = true | ||||
							
								
								
									
										63
									
								
								src/Warm-Ups/Slide Rules/parts/0 logarithms.tex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/Warm-Ups/Slide Rules/parts/0 logarithms.tex
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | ||||
| \section{Logarithms} | ||||
|  | ||||
| \definition{}<logdef> | ||||
| The \textit{logarithm} is the inverse of the exponent. That is, if $b^p = c$, then $\log_b{c} = p$. \\ | ||||
| In other words, $\log_b{c}$ asks the question ``what power do I need to raise $b$ to to get $c$?'' \\ | ||||
|  | ||||
| \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.5] | ||||
| 			\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. | ||||
| \end{instructornote} | ||||
|  | ||||
| \pagebreak | ||||
							
								
								
									
										43
									
								
								src/Warm-Ups/Slide Rules/parts/1 intro.tex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/Warm-Ups/Slide Rules/parts/1 intro.tex
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										299
									
								
								src/Warm-Ups/Slide Rules/parts/2 multiplication.tex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								src/Warm-Ups/Slide Rules/parts/2 multiplication.tex
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										
											BIN
										
									
								
								src/Warm-Ups/Slide Rules/resources/rule.pdf
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/Warm-Ups/Slide Rules/resources/rule.pdf
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										24144
									
								
								src/Warm-Ups/Slide Rules/resources/rule.svg
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										24144
									
								
								src/Warm-Ups/Slide Rules/resources/rule.svg
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 862 KiB | 
							
								
								
									
										534
									
								
								src/Warm-Ups/Slide Rules/sliderule.sty
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										534
									
								
								src/Warm-Ups/Slide Rules/sliderule.sty
									
									
									
									
									
										Executable 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); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user