# `Ascii.JSMath`
[🔗](https://github.com/houllette/ascii_art/blob/main/lib/ascii/js_math.ex#L1)

JavaScript's number semantics where they differ from Elixir's.

`Math.round` (halves up, toward +infinity), `%` (the dividend's sign),
`Math.fround`, typed-array stores (`u8/1`, `clamp_u8/1`, `i8/1`, ...),
`Number.prototype.toFixed` and `String(number)`, V8's `Math.hypot` (which
scales and sums with Kahan's compensation), and two functions `:math` does
not have, `Math.cbrt` and `Math.log1p`, as fdlibm computes them. All of
these agree with V8 bit for bit.

For `Math.sin`, `Math.exp`, `Math.pow` and the other transcendental
functions the pieces call `:math` directly. It uses the platform's libm,
which can differ from V8's fdlibm in the last bit for a few percent of
inputs; a character rarely sits that close to a threshold, and every
piece's golden frames still match upstream's (see PORTING.md).

Erlang floats have no infinity and no NaN. Where JS would answer with one,
these functions raise `ArithmeticError`, as `:math` and Erlang arithmetic
do, and the pieces guard such cases where upstream relies on them.

Rounding functions (`round/1`, `floor/1`, `ceil/1`, `trunc/1`) return
integers, which is what the pieces index with.

# `cbrt`

```elixir
@spec cbrt(x :: number()) :: float()
```

`Math.cbrt` (fdlibm).

# `ceil`

```elixir
@spec ceil(x :: number()) :: integer()
```

`Math.ceil`, as an integer.

# `clamp_u8`

```elixir
@spec clamp_u8(x :: number()) :: 0..255
```

A write to a `Uint8ClampedArray`: clamped to 0..255, halves to even.

# `floor`

```elixir
@spec floor(x :: number()) :: integer()
```

`Math.floor`, as an integer.

# `fround`

```elixir
@spec fround(x :: number()) :: float()
```

`Math.fround`: the nearest single-precision value, as a `Float32Array` stores it.

# `hypot`

```elixir
@spec hypot(a :: number(), b :: number()) :: float()
```

`Math.hypot`, as V8 computes it: each term scaled by the largest and summed
with Kahan's compensation. Not the same bits as `sqrt(a*a + b*b)`.

# `hypot`

```elixir
@spec hypot(a :: number(), b :: number(), c :: number()) :: float()
```

`Math.hypot` of three numbers: `hypot_list/1`'s steps, unrolled.

# `hypot_list`

```elixir
@spec hypot_list(xs :: [number()]) :: float()
```

`Math.hypot` of any count of numbers.

# `i8`

```elixir
@spec i8(x :: number()) :: -128..127
```

A write to an `Int8Array`.

# `i16`

```elixir
@spec i16(x :: number()) :: -32768..32767
```

A write to an `Int16Array`.

# `log1p`

```elixir
@spec log1p(x :: number()) :: float()
```

`Math.log1p` (fdlibm).

# `mod`

```elixir
@spec mod(a :: number(), b :: number()) :: number()
```

JS `%`: the remainder of truncated division, with the dividend's sign
(C's `fmod`). Integers stay integers.

# `round`

```elixir
@spec round(x :: number()) :: integer()
```

`Math.round`: halves round up, toward +infinity. Returns an integer.

# `sign`

```elixir
@spec sign(x :: number()) :: -1 | 0 | 1
```

`Math.sign`: -1, 0 or 1.

# `sqrt`

```elixir
@spec sqrt(x :: number()) :: float()
```

`Math.sqrt`, exactly rounded on every platform.

# `to_fixed`

```elixir
@spec to_fixed(x :: number(), digits :: non_neg_integer()) :: String.t()
```

`Number.prototype.toFixed`: rounds the exact binary value, halves away from
zero, which is not what `:erlang.float_to_binary/2` does on a tie.

# `to_string`

```elixir
@spec to_string(x :: number()) :: String.t()
```

`String(x)` for a number: the shortest digits that read back as `x`, laid out as JS does.

# `trunc`

```elixir
@spec trunc(x :: number()) :: integer()
```

`Math.trunc`, as an integer.

# `u8`

```elixir
@spec u8(x :: number()) :: 0..255
```

A write to a `Uint8Array`: truncated, then wrapped modulo 256.

# `u16`

```elixir
@spec u16(x :: number()) :: 0..65535
```

A write to a `Uint16Array`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
