Calculator: exponentiation and modulo #3

Closed
opened 2026-09-28 11:10:25 +02:00 by clawbot · 2 comments
Collaborator

Part of #2. Independent of the API units.

What. The calculator accepts ^ (also written **) for exponentiation and % for modulo, with the usual precedence, alongside what it does now.

Decisions (reversible, taken by the repo-manager):

  • ^ and ** are one operator. It binds tighter than *, /, % and than a sign on its left, and groups to the right: 2^3^2 is 512, -2^2 is -4, (-2)^2 is 4, 2^-1 is 0.5.
  • % sits with * and /, left to right. The result takes the sign of the divisor, as in Python and most calculators: 7 % 3 is 1, -7 % 3 is 2, 7 % -3 is -2. It works on decimals, exactly: 7.5 % 2 is 1.5, 0.3 % 0.1 is 0. x % 0 is division by zero.
  • A whole-number exponent is computed exactly: 0.1^2 is 0.01, 2^100 - 2^100 + 1 is 1. A fractional exponent is computed in float64: 2^0.5 is 1.4142135623730951. A negative base with a fractional exponent has no real result and gets its own reply. 0^0 is 1; zero to a negative power is division by zero.
  • Bounded work: an exact power whose result would pass a fixed size (for example 4096 bits of numerator plus denominator, estimated before multiplying) is computed in float64 instead; a float result that overflows is refused as too large, as now. A test shows the worst cases that fit in 256 bytes (such as 9^9^9^9^9, ((9^999)^999)^999, 1.0000001^99999) answer promptly.
  • go/parser cannot express this (^ is XOR at the precedence of +, and ** does not parse), and neither the standard library nor the default package list parses exact arithmetic with a power operator. So internal/calc gets a small tokenizer and precedence-climbing parser of its own, still computing with go/constant. Keep the decimal-only number rule (010 is ten; no hex, _ or i), the 256-byte limit, and refusal of everything else. Small and plain: a newcomer follows it in one reading.

Done when:

  • internal/calc tests cover precedence, grouping, signs, every example above, the refusals and the bounded-work cases; the existing tests still pass, changed only where the parser change must change them.
  • bot.Reply's "I only understand arithmetic" sentence and the welcome text mention ^ and %; the no-real-result case has its own sentence.
  • README: the introduction and the Arithmetic design bullet say how to write powers and modulo, with examples. docs/TODO.md Completed Steps gets a line.
  • make check is green.

Model: opus-5-5

Part of https://git.eeqj.de/sneak/simplexcalc/issues/2. Independent of the API units. **What.** The calculator accepts `^` (also written `**`) for exponentiation and `%` for modulo, with the usual precedence, alongside what it does now. **Decisions** (reversible, taken by the repo-manager): - `^` and `**` are one operator. It binds tighter than `*`, `/`, `%` and than a sign on its left, and groups to the right: `2^3^2` is `512`, `-2^2` is `-4`, `(-2)^2` is `4`, `2^-1` is `0.5`. - `%` sits with `*` and `/`, left to right. The result takes the sign of the divisor, as in Python and most calculators: `7 % 3` is `1`, `-7 % 3` is `2`, `7 % -3` is `-2`. It works on decimals, exactly: `7.5 % 2` is `1.5`, `0.3 % 0.1` is `0`. `x % 0` is division by zero. - A whole-number exponent is computed exactly: `0.1^2` is `0.01`, `2^100 - 2^100 + 1` is `1`. A fractional exponent is computed in float64: `2^0.5` is `1.4142135623730951`. A negative base with a fractional exponent has no real result and gets its own reply. `0^0` is `1`; zero to a negative power is division by zero. - Bounded work: an exact power whose result would pass a fixed size (for example 4096 bits of numerator plus denominator, estimated before multiplying) is computed in float64 instead; a float result that overflows is refused as too large, as now. A test shows the worst cases that fit in 256 bytes (such as `9^9^9^9^9`, `((9^999)^999)^999`, `1.0000001^99999`) answer promptly. - `go/parser` cannot express this (`^` is XOR at the precedence of `+`, and `**` does not parse), and neither the standard library nor the default package list parses exact arithmetic with a power operator. So `internal/calc` gets a small tokenizer and precedence-climbing parser of its own, still computing with `go/constant`. Keep the decimal-only number rule (`010` is ten; no hex, `_` or `i`), the 256-byte limit, and refusal of everything else. Small and plain: a newcomer follows it in one reading. **Done when:** - `internal/calc` tests cover precedence, grouping, signs, every example above, the refusals and the bounded-work cases; the existing tests still pass, changed only where the parser change must change them. - `bot.Reply`'s "I only understand arithmetic" sentence and the welcome text mention `^` and `%`; the no-real-result case has its own sentence. - README: the introduction and the Arithmetic design bullet say how to write powers and modulo, with examples. `docs/TODO.md` Completed Steps gets a line. - `make check` is green. Model: opus-5-5
clawbot added a new dependency 2026-09-28 11:10:29 +02:00
clawbot self-assigned this 2026-09-28 11:10:30 +02:00
Author
Collaborator

Built in #11: ^ (also **) and % in internal/calc on a parser of its own, with the replies, the README and docs/TODO.md updated.

One addition beyond the decisions above: go/constant rounds a number whose numerator or denominator reaches 4096 bits. A remainder of such a number, or the sign of a negative number raised to it, would come out wrong, so both are refused as too large.

Model: opus-5-5

Built in https://git.eeqj.de/sneak/simplexcalc/pulls/11: `^` (also `**`) and `%` in `internal/calc` on a parser of its own, with the replies, the README and `docs/TODO.md` updated. One addition beyond the decisions above: `go/constant` rounds a number whose numerator or denominator reaches 4096 bits. A remainder of such a number, or the sign of a negative number raised to it, would come out wrong, so both are refused as too large. Model: opus-5-5
Author
Collaborator

Decision after two reviews of #11 found wrong answers at the edges of what can be held:

  • A result is either exact (held by go/constant below the 4096-bit limit), or computed in float64 and inside the normal range of a double (about 2.2e-308 to 1.8e308, or zero because the true answer is zero). Anything else is refused, with a reply true for both too large and too small.
  • A whole-number exponent is computed exactly unless the exact result would pass the 4096-bit limit in either direction, so 2^-1400 is exact.
  • Ordinary float64 rounding carried into later arithmetic (1.01^5000 % 7) is accepted, as both reviews judged.

Model: opus-5-5

**Decision** after two reviews of https://git.eeqj.de/sneak/simplexcalc/pulls/11 found wrong answers at the edges of what can be held: - A result is either exact (held by `go/constant` below the 4096-bit limit), or computed in float64 and inside the normal range of a double (about 2.2e-308 to 1.8e308, or zero because the true answer is zero). Anything else is refused, with a reply true for both too large and too small. - A whole-number exponent is computed exactly unless the exact result would pass the 4096-bit limit in either direction, so `2^-1400` is exact. - Ordinary float64 rounding carried into later arithmetic (`1.01^5000 % 7`) is accepted, as both reviews judged. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: sneak/simplexcalc#3