#import "@local/handout:0.1.0": *
#import "@preview/cetz:0.4.2"
#import "@preview/cetz-plot:0.1.2": chart, plot

= 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)
