# Math.Abs, Math.Sign, Math.Min, Math.Max, Math.Clamp, Math.Truncate, Math.Pow, Math.Sqrt, Math.Sin, Math.Cos, Math.Tan

> The numeric helpers, each returning the type C# says it returns. Abs, Min, Max and Clamp answer in the WIDEST of their arguments, so a decimal stays exact and a double stays a double. The rounding family answers a decimal for a decimal and a double otherwise. Pow and Sqrt are always double — C# gives them no other overload. Sign is an int. Truncate rounds toward zero; Clamp throws if its bounds are inverted.

<!-- id: function-math · area: function · stability: stable · html: https://osysharp.com/reference/function/math/ -->

## Summary        {#summary}
`Math.Abs`, `Math.Sign`, `Math.Min`, `Math.Max`, `Math.Clamp`, `Math.Truncate`, `Math.Pow` and `Math.Sqrt`
are the numeric helpers you reach for in a calculation. They join the rounding functions (`Math.Round`,
`Math.Floor`, `Math.Ceiling`), and like those they are pinned to the exact answer this platform's server
produces — which is not always what a bare double would give.

## Signature      {#signature}
```osy syntax
Math.Abs(<number> x)      -> decimal      // magnitude
Math.Sign(<number> x)     -> int          // -1, 0, or 1
Math.Min(<number> a, <number> b) -> decimal
Math.Max(<number> a, <number> b) -> decimal
Math.Clamp(<number> x, <number> min, <number> max) -> decimal
Math.Truncate(<number> x) -> decimal      // drop the fraction, toward zero
Math.Pow(<number> x, <number> y) -> decimal
Math.Sqrt(<number> x)     -> decimal

Math.Sin(<number> radians)  -> double     // the trig family is ALWAYS double
Math.Cos(<number> radians)  -> double
Math.Tan(<number> radians)  -> double
Math.Asin(<number> value)   -> double     // the inverses, answering RADIANS
Math.Acos(<number> value)   -> double
Math.Atan(<number> value)   -> double
Math.Atan2(<number> y, <number> x) -> double   // note the order: y first

Math.Log(<number> value)   -> double      // natural log, base e
Math.Log(<number> value, <number> newBase) -> double   // note the order: VALUE first
Math.Log10(<number> value) -> double
Math.Log2(<number> value)  -> double
Math.Exp(<number> value)   -> double      // e raised to the power — Log's inverse

Math.PI                    -> double      // 3.141592653589793 — a CONSTANT, no parentheses
Math.E                     -> double      // 2.718281828459045
```

## Description    {#description}

### The result type follows the argument, as it does in C#   {#result-type}
There are three rules, and each is the one C#'s own overloads give:

- **`Abs`, `Min`, `Max`, `Clamp` answer in the WIDEST of their arguments.** `Math.Abs(-5)` is an `int`;
  `Math.Abs(-2.5)` is a `double`; `Math.Abs(-2.50m)` is a `decimal`, keeping its digits and its scale. So a
  chain of decimal arithmetic stays exact ([decimal](https://osysharp.com/reference/types/decimal/)) and a chain of double arithmetic stays a double.
- **`Round`, `Floor`, `Ceiling`, `Truncate` answer a `decimal` for a decimal, and a `double` otherwise.** C#
  has both overloads; the `int` case is genuinely ambiguous in C# (it will not compile without a cast) and
  resolves to `double` here.
- **`Pow` and `Sqrt` are always `double`**, and **`Sign` is always an `int`** — a sign is not a quantity.
- **The logarithms — `Log`, `Log10`, `Log2` — and `Exp` are always `double`**, for the same reason: C# gives them
  no decimal overload, and a logarithm feeds straight back into arithmetic, where one decimal operand would promote
  the whole expression.
- **The trig family — `Sin`, `Cos`, `Tan`, `Asin`, `Acos`, `Atan`, `Atan2` — is always `double`**, whatever you
  pass. See below for why that one is not a free choice.

### The trig family is double, and that is load-bearing   {#trig}
`Math.Sin` and friends take an angle in **radians** and answer a `double`; the inverses (`Math.Asin`, `Math.Acos`,
`Math.Atan`, `Math.Atan2`) take a ratio and answer an angle in radians. Degrees are never implied anywhere — convert
them yourself (`degrees * Math.PI / 180`) if that is what you have.

They cannot answer a `decimal`, and the reason is arithmetic rather than taste. A trig result goes straight back into
arithmetic — `dirX * cos - dirY * sin`, every frame — and a single `decimal` operand promotes the whole expression to
decimal. A decimal has a bounded exponent, so a chain of reciprocals over promoted values fails outright with *"value
was either too large or too small for a Decimal"*. Exactness is the right default for money and the wrong tool for a
rotation matrix.

⚠ **`Math.Atan2` takes `(y, x)`, in that order** — C#'s own, and the reverse of what the name suggests on first
reading. It is the one that answers "what bearing is this vector" correctly in all four quadrants, which
`Math.Atan(y / x)` cannot: dividing first throws away the sign information and folds two quadrants onto two others.

### The two constants   {#constants}
`Math.PI` and `Math.E` are **constants, written without parentheses** — the C# spelling. They are `double`, like
everything they feed.

They matter most for the trig family above, which takes radians: an angle is a multiple of π, so before these existed
a degrees-to-radians conversion had to paste the literal `3.141592653589793` at every call site. (This page told you
to do exactly that.)

### The logarithms   {#logarithms}

`Math.Log(x)` is the **natural** log (base e), matching C#. `Math.Log10` and `Math.Log2` are the two bases with
their own methods — prefer them to `Log(x) / Log(10)`, which is one rounding step worse. `Math.Exp` is `Log`'s
inverse, so `Math.Log(Math.Exp(x))` is `x` to within floating-point tolerance.

⚠ **`Math.Log(value, newBase)` takes the VALUE first** — `Math.Log(8, 2)` is `3`, not `⅓`. Both arguments are bare
numbers, so a transposition produces a number rather than an error, and nothing downstream will look wrong.

Every one of these runs on the **client** as well as the server, and lowers into a compiled `on frame` body — a
log-scale axis recomputes one per tick per render, so a round trip for it would be absurd.

⚑ **Comparing these in a test needs `Assert.Equal(expected, actual, precision)`** — the third argument is the number
of decimal places both sides are rounded to. `Math.Sin(Math.PI)` is not exactly 0 and `Math.Log(Math.Exp(2))` is not
exactly 2 in any language, because π and e are not representable; the tolerance is not sloppiness, it is the only
correct way to assert a transcendental.

⚠ **This changed.** These used to return a `decimal` whatever you passed. It was a bug rather than a policy:
the type system already declared the rules above and only the runtimes disagreed. If you were relying on
`Math.Floor(someInt)` handing back a decimal, it now hands back a double of the same value.

**Why it mattered.** A `decimal` operand promotes its whole expression, so a single `Math.Abs` inside otherwise
`double` arithmetic quietly turned the rest of the calculation into decimal — and a decimal has a bounded
exponent, so a chain of reciprocals eventually failed with *"value was either too large or too small for a
Decimal"*. Exactness is what you want for money and the wrong tool for geometry.

### Abs, Sign, and Truncate   {#abs-sign-truncate}
`Math.Abs(x)` is the magnitude; `Math.Sign(x)` is `-1`/`0`/`1`. `Math.Truncate(x)` drops the fraction
**toward zero**, so `Math.Truncate(-2.9)` is `-2` — this is the difference from `Math.Floor`, which goes
toward negative infinity and gives `-3`. For a value already positive the two agree.

### Min, Max, and Clamp   {#min-max-clamp}
`Math.Min`/`Math.Max` return the smaller/larger of two numbers. `Math.Clamp(x, min, max)` pins `x` into the
`[min, max]` range — below `min` it returns `min`, above `max` it returns `max`. If you invert the bounds so
`min > max`, `Math.Clamp` **throws**: there is no range to clamp into, and returning a silent wrong value
would be worse. (When two values are numerically equal but differ in scale — `1.5` vs `1.50` — which one
`Min`/`Max` returns is fixed but rarely matters; use a format specifier if the displayed scale is important.)

### Pow and Sqrt are doubles, and are not exact   {#pow-sqrt}
`Math.Pow(x, y)` and `Math.Sqrt(x)` have **no exact decimal form** for most inputs — C# gives them no decimal
overload at all — so they are computed in binary floating point and **stay** there. `Math.Sqrt(2)` is
`1.4142135623730951`, the full float64 answer, identical in the browser and on the server.

Do not use them where you need the last cent to be provably right: a square root of a price is not a price.
Convert deliberately if you need to come back to money.

These are pure functions of their arguments, so they run **in the browser** with no round trip where a UI
action needs them ([execution side](https://osysharp.com/reference/function/execution-side/)).

## Examples       {#examples}
```osy title="a bounded, rounded score" test app=math
// Clamp a raw score into range, then keep two decimals for display.
decimal Score(decimal raw) {
  return Math.Round(Math.Clamp(raw, 0m, 100m), 2);
}
```

```osy title="the exact answers, pinned" run app=math
[Test]
void Math_answers() {
  Assert.Equal(87.35m, Score(87.347m));
  Assert.Equal(100m, Score(140m));            // clamped to the ceiling
  Assert.Equal(0m, Score(-5m));               // clamped to the floor

  Assert.Equal(5, Math.Abs(-5));              // int in, INT out — the widest argument wins
  Assert.Equal(2.50m, Math.Abs(-2.50m));      // decimal in, decimal out — scale kept
  Assert.Equal(1, Math.Sign(2.5m));           // Sign returns an int
  Assert.Equal(0, Math.Sign(0m));
  Assert.Equal(-1, Math.Sign(-0.0001m));

  Assert.Equal(-2m, Math.Truncate(-2.9m));    // toward zero…
  Assert.Equal(-3m, Math.Floor(-2.9m));       // …unlike Floor

  Assert.Equal(3, Math.Min(3, 7));
  Assert.Equal(7, Math.Max(3, 7));
  Assert.Equal(3, Math.Clamp(5, 1, 3));

  Assert.Equal(1024.0, Math.Pow(2, 10));      // Pow and Sqrt are DOUBLES, always
  Assert.Equal(4.0, Math.Sqrt(16));

  Assert.Equal(3.0, Math.Log10(1000));        // the logs are doubles too
  Assert.Equal(3.0, Math.Log2(8));
  Assert.Equal(3.0, Math.Log(8, 2));          // VALUE first — Log(2, 8) would be 0.333…
  Assert.Equal(1.0, Math.Exp(0));

  // The constants — no parentheses.
  Assert.Equal(3.141592653589793, Math.PI);
  Assert.Equal(2.718281828459045, Math.E);

  // …and the functions, which are never EXACTLY their mathematical answer: the third argument is the number of
  // decimal places both sides are rounded to before comparing.
  Assert.Equal(0.0, Math.Sin(Math.PI), 12);
  Assert.Equal(2.0, Math.Log(Math.Exp(2)), 12);
}
```

### Can I pass a string to `Math.*`?   {#string-arguments}
`Math.*` will coerce a **string** argument, and it reads it with the platform's one numeric-coercion grammar —
the same one [Convert](https://osysharp.com/reference/function/convert/) documents, including its `0` for text it cannot read. `Math.Abs("-1,000")` is
`1000`; `Math.Abs("(5)")` is `0`, not `5`. If the value came from a form field, convert it deliberately first
(`Convert.ToDecimal`) so the failure is visible where it happens rather than inside the arithmetic.

## See also       {#see-also}
- [decimal](https://osysharp.com/reference/types/decimal/) — what a decimal argument buys you, and where exactness matters
- [format specifiers](https://osysharp.com/reference/function/format-specifiers/) — rounding for DISPLAY (`F2`, `N0`), distinct from `Math.Round`
- [Convert](https://osysharp.com/reference/function/convert/) — coercing between numeric types
