# Improving Machine Arithmetic

**URL:** <https://discourse.haskell.org/t/improving-machine-arithmetic/13602>\
**Category:** Uncategorized\
**Created:** [January 25, 2026, 12:12am UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602 "2026-01-25T00:12:43Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![mniip](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/mniip/32/4033_2.png) [@mniip](https://discourse.haskell.org/u/mniip)\
**Post date:** [January 25, 2026, 12:12am UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/1 "2026-01-25T00:12:43Z")

</div>

Every once in a while I set out to write some code that involves e.g. some nontrivial index arithmetic, and I find the available toolbox for machine arithmetic clearly lacking. To be specific, I don’t mean any advanced number theory or anything, but rather simple things like addition and multiplication, viewed through the prism of representing mathematical integers on a computer: dealing with overflow, rounding, and other possible limitations of said representation.

For example, when adding two `Int`s, it can happen that the result is not itself representable as an `Int`: there is an “overflow” or “underflow”. Being a haskeller, I care about program correctness, and how my program responds to overflow is an important aspect of it. In different circumstances I may intend it to respond differently, here’s a few really common ways:

- Wrapping: perform the addition modulo `2^bitwidth` (which in the case of `Int` being a signed type implies using 2’s complement);
- Trapping: if the addition was unsuccessful, terminate the entire computation (`error`).
- Checked: let me branch (pattern match) on whether the addition was successful or not, potentially also let me branch on whether it was overflow or underflow, etc;
- Saturating: return the `Int` that is closest to the mathematical result, meaning `maxBound` in case of overflow, and `minBound` in case of underflow.
- Undefined: trust me it’s not going to happen, generate the fastest implementation possible with this assumption.

The `base` library does not provide any way for me to specify that I want one of these things to happen. Worse yet, the functions that `base` provides aren’t even documented to belong to one of these categories. Most operations such as `(+) @Int` are wrapping, but this is not documented anywhere. `fromInteger @Natural` cannot be wrapping so it is trapping, and so is `fromEnum @Natural`. But `fromEnum @Integer` is wrapping.

Still, the program needs to be written, and while one could make pedantically correct implementations of those behaviors to the tune of:

```hs
let result = toInteger @Int a + toInteger @Int b
in if
  | result > toInteger @Int maxBound
  -> -- handle overflow
  | result < toInteger @Int minBound
  -> -- handle underflow
  | otherwise
  -> -- use `fromInteger @Int result` which is now guaranteed to be safe

```

these would be embarrassingly slow. And besides program correctness I care about program performance. So what often ends up happening (I’ve seen it in my code and in others’), is that you reach for `GHC.Prim.addIntC#` and reinvent the wheel, over and over again.

This situation is basically on par with C, where there’s only one `+`, it does _something_ and you have to always keep in mind what; and where the recommended trick (!) for detecting overflow is `a < a + b`.

In the long term I would like the situation in `base` to improve, but in the interim a package would be great too. Having a concrete and proven-usable interface is a great vehicle for getting changes merged into `base`.

One of the goals is uncompromising performance, and the implementations may be tricky to optimize, so it would be better to have a centralized place where these optimizations would live, rather than everyone optimizing their own wheel they reinvented. Notably, said optimizations will necessarily have to interact closely with the compiler internals, may require new primops, so maybe having them be maintained inside the GHC source tree would be better? On the other hand, introducing a bunch of primops for which we don’t have a good interface may not be a good idea either.

My search on hackage for prior art only found [`checked`](https://hackage.haskell.org/package/checked), likely hopelessly outdated. The closest in spirit is actually [`integer-logarithms`](https://hackage.haskell.org/package/integer-logarithms), but that only provides logarithms and powers.

I would like to invite some discussion about this topic, what such a package could look like, what changes to `base` could look like, etc. Here’s some of my initial thoughts:

- Prior to talking about typeclasses or other overloaded interfaces, we should talk about the monomorphic functions we’re trying to support. Having to explicitly spell out `checkedAddInt32ToInt32` is better than having nothing to call at all because you couldn’t fit this function in a typeclass that mandates that the type must be `a -> a -> a`.
- We may need more complex functions than the basic binary operators, but it’s difficult to define the exact scope.
  - Some operations can be composed out of more basic building blocks, while others may not. For example, consider that a saturating multiply-add is different from a saturating multiply followed by a saturating add. But in wrapping mode they are the same. For another example, unsigned multiply-then-modulo never overflows, but is different than a wrapping multiply followed by a modulo.
  - There’s always the reference implementation going through `Integer`. Some operations might admit much more performant implementations, while others may not. For example, unsigned multiply-then-modulo is like 2 instructions on `x86_64`.
  - Some operations may have potential applications, while others do not.

- Heterogeneous operations such as adding signed to unsigned, absolute difference, 64x64-\>128 bit multiplication – are also important.
- It’s not clear to do with portability and in particular the `Int` size story. Depending on the platform it’s equivalent to either `Int32` or `Int64`. And then there’s ancient comments in `GHC.Prim` about how an `Int` can be 30 bits due to tagging.
- We should look to other standard libraries for inspiration:
  - Rust: by far the best example, [u32](https://doc.rust-lang.org/nightly/std/primitive.u32.html) has methods `add`, `strict_add`, `checked_add`, `carrying_add`, `wrapping_add`, `unchecked_add`, `saturating_add`, `overflowing_add`. There’s also a lot of non-basic operations worth considering.
  - C++: has `add_sat` and `ckd_add`. Also `midpoint` is a non-basic operation worth considering.
  - Zig: [Integer Overflow](https://ziglang.org/documentation/0.15.2/#Integer-Overflow), there’s builtin operators `+`, `+%`, `+|`, operations `@addWithOverflow` and `math.add`.

---

<div class="post-metadata">

**Author:** ![Bodigrim](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/bodigrim/32/1457_2.png) [@Bodigrim](https://discourse.haskell.org/u/Bodigrim)\
**Post date:** [January 25, 2026, 12:28am UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/2 "2026-01-25T00:28:44Z")

</div>

For prior art there is [int-cast: Checked conversions between integral types](https://hackage.haskell.org/package/int-cast) for safe casts. Also `bytestring` goes some length to catch overflows, grep for `checkedAdd`.

---

<div class="post-metadata">

**Author:** ![Bodigrim](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/bodigrim/32/1457_2.png) [@Bodigrim](https://discourse.haskell.org/u/Bodigrim)\
**Post date:** [January 25, 2026, 12:30am UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/3 "2026-01-25T00:30:03Z")

</div>

Haskell packages can contain not only Haskell, but also CMM, which gets you very close to “your own primops” if you need, without hacking them inside GHC.

---

<div class="post-metadata">

**Author:** ![mchav](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/mchav/32/5143_2.png) [@mchav](https://discourse.haskell.org/u/mchav)\
**Post date:** [January 25, 2026, 8:49am UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/4 "2026-01-25T08:49:20Z")

</div>

This would extend to other integral types in `Data.Int`, right?

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [January 25, 2026, 9:06am UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/5 "2026-01-25T09:06:18Z")

</div>

> [@Bodigrim](#):
>
> Haskell packages can contain not only Haskell, but also CMM

That’s interesting. Are you referring to this [`inline-cmm`](https://github.com/jhhuh/inline-cmm) library? Or maybe separate `.cmm` source files?

---

<div class="post-metadata">

**Author:** ![Bodigrim](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/bodigrim/32/1457_2.png) [@Bodigrim](https://discourse.haskell.org/u/Bodigrim)\
**Post date:** [January 25, 2026, 9:29am UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/6 "2026-01-25T09:29:21Z")

</div>

Just `.cmm` files, they can be included under `cmm-sources:` in Cabal file.

---

<div class="post-metadata">

**Author:** ![Kleidukos](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/kleidukos/32/1213_2.png) [@Kleidukos](https://discourse.haskell.org/u/Kleidukos)\
**Post date:** [January 25, 2026, 11:37am UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/7 "2026-01-25T11:37:02Z")

</div>

See this example: [atomic-counter/Counter.cmm at master · sergv/atomic-counter · GitHub](https://github.com/sergv/atomic-counter/blob/master/Counter.cmm)

---

<div class="post-metadata">

**Author:** ![TerrorJack](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/terrorjack/32/5155_2.png) [@TerrorJack](https://discourse.haskell.org/u/TerrorJack)\
**Post date:** [January 25, 2026, 1:17pm UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/8 "2026-01-25T13:17:49Z")

</div>

speaking of checked arithmetic in ghc, a major problem is that it’s not comprehensive; it’s better to have:

- explicitly sized int/word operand types
- high level base library wrappers

it’s possible to diy one’s own checked arithmetic implementation using regular primops (e.g. see [`genericIntAddCOp`](https://gitlab.haskell.org/ghc/ghc/-/blob/master/compiler/GHC/StgToCmm/Prim.hs#L2203) in the compiler), but still it’s better to have primop coverage and different backends may be able to emit more efficient code.

@Kleidukos has already opened [Adding checked arithmetic low-level functions (#22665) · Issues · Glasgow Haskell Compiler / GHC · GitLab](https://gitlab.haskell.org/ghc/ghc/-/issues/22665) a few years ago about this topic, thanks!

---

<div class="post-metadata">

**Author:** ![augustss](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/augustss/32/887_2.png) [@augustss](https://discourse.haskell.org/u/augustss)\
**Post date:** [January 26, 2026, 11:53am UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/9 "2026-01-26T11:53:30Z")

</div>

I agree with you. The Haskell report (and GHC) leaves it unspecified what happens on overflow. This was an intentional choice 35 years ago (in the name of efficiency). So that means that Haskell arithmetic is, sadly, in your last category “Undefined”.

I’m in the middle of changing MicroHs to do trapping arithmetic. It’s definitely not without prooblems, e.g., `(-128)::Int` overflows in the negation since it is `negate (fromInteger @Int8 128)`. But I will give it a shot and see what happens. As with most things in MicroHs, it willk be an optional feature.

It would be great to have primitives for all the variants that you suggest. And we also should work towards making a better choice for the default.

---

<div class="post-metadata">

**Author:** ![olf](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/olf/32/3813_2.png) [@olf](https://discourse.haskell.org/u/olf)\
**Post date:** [January 26, 2026, 4:50pm UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/10 "2026-01-26T16:50:46Z")

</div>

> [@augustss](#):
>
> So that means that Haskell arithmetic is, sadly, in your last category “Undefined”.
> 
> I’m in the middle of changing MicroHs to do trapping arithmetic.

I would consider the future behaviour of MicroHS a bug. The documentation of `Num` says that “`(+)` and `(*)` are customarily expected to define a ring” which implies that the operations are total.

---

<div class="post-metadata">

**Author:** ![augustss](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/augustss/32/887_2.png) [@augustss](https://discourse.haskell.org/u/augustss)\
**Post date:** [January 26, 2026, 5:14pm UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/11 "2026-01-26T17:14:03Z")

</div>

So you’d rather have arithmetic that silently gives you the “wrong” answer rather than scream at you? I don’t.

Since there are already several instances (`Double`, `Float`, `Natural`) that don’t obey the ring axioms, I feel that I’m in good company.

---

<div class="post-metadata">

**Author:** ![olf](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/olf/32/3813_2.png) [@olf](https://discourse.haskell.org/u/olf)\
**Post date:** [January 26, 2026, 5:28pm UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/12 "2026-01-26T17:28:06Z")

</div>

> [@augustss](#):
>
> Since there are already several instances (`Double`, `Float`, `Natural`) that don’t obey the ring axioms, I feel that I’m in good company.

I consider that as the core problem: How rigorously are we expecting the already weakly stated arithmetic laws to hold? Even `numeric-prelude` falsely claims that `Double` is a ring. One way forward would be to have new typeclasses with high entry barriers for membership, e.g. `class HonestToGodRingWithTotalAndLawfulOperations` of which `Integer` but not `Double` can be a member.

---

<div class="post-metadata">

**Author:** ![ApothecaLabs](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/apothecalabs/32/3215_2.png) [@ApothecaLabs](https://discourse.haskell.org/u/ApothecaLabs)\
**Post date:** [January 26, 2026, 7:19pm UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/13 "2026-01-26T19:19:12Z")

</div>

See, I think part of the problem is that Haskell not only conflates eg the concept of a binary operation of a lawful ring / additive group / etc (which kind of has the same problems as having a privileged Monoid instance) with that of a more generalized concept of a forgetful / exceptional function that only approximates it within certain limits (I do support better checked arithmetic) - but we are also conflating the function ‘add’ with the symbolic operator ‘+’, because Haskell `Num` requires defining it _as_ `(+)` which not only locks fallible numeric addition behind lawful additive groups, but it also steals the syntactic operator which requires hiding the entire `Num` hierarchy to deal with - thus partly contributing to the frankly rather insane number of custom operators endemic to advanced Haskell.

---

<div class="post-metadata">

**Author:** ![dandart](https://avatars.discourse-cdn.com/v4/letter/d/8e8cbc/32.png) [@dandart](https://discourse.haskell.org/u/dandart)\
**Post date:** [January 29, 2026, 1:56pm UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/14 "2026-01-29T13:56:16Z")

</div>

I feel like this lends itself to newtypes for deriving via, e.g.:

```haskell
newtype OverflowStrategy a = OverflowStrategy a

instance (Num a) => Num (OverflowStrategy a) where
-- ... do the overflow here ...

-- etc other strategies...

newtype MyCustomIntegral a = MyCustomIntegral a
    deriving Num via (OverflowStrategy Int) -- or whichever would compile...

```

or equivalent, then using coerce to go between them.

Maybe a library like that exists, maybe it’s worth experimenting with, or getting something like this into base.

Cheers

---

<div class="post-metadata">

**Author:** ![konsumlamm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/konsumlamm/32/3193_2.png) [@konsumlamm](https://discourse.haskell.org/u/konsumlamm)\
**Post date:** [January 29, 2026, 2:25pm UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/15 "2026-01-29T14:25:02Z")

</div>

Rust actually does this, it has the [`Saturating`](https://doc.rust-lang.org/std/num/struct.Saturating.html) and [`Wrapping`](https://doc.rust-lang.org/std/num/struct.Wrapping.html) newtypes for saturating and wrapping arithmetic, respectively. I think it would work even better in Haskell, since number literals can be overloaded. However the Haskell version of this would require `FlexibleInstances`.

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [January 29, 2026, 2:46pm UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/16 "2026-01-29T14:46:36Z")

</div>

You could avoid `FlexibleInstances` by introducing an extra class:

```haskell
newtype Wrapping a = Wrapping a
class WrappingNum a where
  ...
instance (WrappingNum a) => Num (Wrapping a) where
  ...

```

---

<div class="post-metadata">

**Author:** ![qqwy](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/qqwy/32/1983_2.png) [@qqwy](https://discourse.haskell.org/u/qqwy)\
**Post date:** [February 2, 2026, 2:36pm UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/17 "2026-02-02T14:36:23Z")

</div>

This is a great idea. I am very much for adding these as proper primitive functions to the language!

Two years ago, during ZuriHac 2024, I was working on a related concept: the [Size](https://github.com/Qqwy/haskell-size/) library, which would be a better default than `Int` when dealing with sizes, lengths, offsets and indexes. The main idea there, is to use a 63-bit (31-bit on 32-bit machines) representation to remain backwards compatible with any `base` function that accepts an `Int`, as well as doing checked arithmetic _in debug and test builds only_, just like Rust.

To properly allow configuring this, I contributed a small feature to GHC to expose whether assertions are enabled or not as a `` __GLASGOW_HASKELL_ASSERTS_IGNORED__ ` CPP macro define ([users guide section](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/assert.html#the-glasgow-haskell-asserts-ignored-cpp-macro), [GitLab issue](https://gitlab.haskell.org/ghc/ghc/-/issues/24967)). It’s in GHC `9.12` and newer.

However, I did not feel very confident in whether my checked arithmetic implementation was correct, and I ran out of spare time available to refine the library, so it is not on Hackage yet. And it also has very limited scope: Only supporting `Word`/`Int` essentially.

I _very_ much agree that improving the set of builtin functions is a good idea. Right now, we do have `addIntC#` for `Int` but not for most other types. And if you want to do safe multiplication, that function follows a completely different naming scheme: `timesInt2#` for some reason. And for many of the other native number types, the functions have either even more cryptic names or they do not exist at all. Let’s fix this!

* * *

Aside:

> And then there’s ancient comments in `GHC.Prim` about how an `Int` can be 30 bits due to tagging.

I dove into the background of this a while back. It’s because of the [Yale Haskell System](https://wiki.haskell.org/index.php?title=Haskell_Lisp) (active between 1991 and 1993 according to the Haskell wiki), which is implemented on top of CMU Common Lisp. Much of the builtin types and garbage collection is outsourced to CMU Common Lisp’s own runtime, which uses [a three-bit tagging scheme,](https://cmucl.org/docs/internals/html/Tagging.html)with two tags reserved for fixnums, leaving you with 30 bits for the number.

EDIT: Pretty sure GHCJS, if run on a 32-bit system, actually breaks this guarantee as binary32 floats only have 23 bits (24 if counting the sign) of integer precision. Not that there are many people combining JavaScript with 32-bit machines anno 2026.

---

<div class="post-metadata">

**Author:** ![konsumlamm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/konsumlamm/32/3193_2.png) [@konsumlamm](https://discourse.haskell.org/u/konsumlamm)\
**Post date:** [February 2, 2026, 5:15pm UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/18 "2026-02-02T17:15:36Z")

</div>

> [@qqwy](#):
>
> EDIT: Pretty sure GHCJS, if run on a 32-bit system, actually breaks this guarantee as binary32 floats only have 23 bits (24 if counting the sign) of integer precision. Not that there are many people combining JavaScript with 32-bit machines anno 2026.

JavaScript running on a 32-bit system still uses binary64 floats, according to the standard.

---

<div class="post-metadata">

**Author:** ![augustss](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/augustss/32/887_2.png) [@augustss](https://discourse.haskell.org/u/augustss)\
**Post date:** [February 2, 2026, 11:05pm UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/19 "2026-02-02T23:05:55Z")

</div>

I really like that Haskell allows an implementation with tagged Ints. I’ve not used it yet, but I’ve considered trying it with MicroHs.

---

<div class="post-metadata">

**Author:** ![lylek](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/lylek/32/2937_2.png) [@lylek](https://discourse.haskell.org/u/lylek)\
**Post date:** [February 3, 2026, 7:12am UTC](https://discourse.haskell.org/t/improving-machine-arithmetic/13602/20 "2026-02-03T07:12:02Z")

</div>

> [@mniip](#):
>
> the recommended trick (!) for detecting overflow is `a < a + b`.

Yeah, that always bugged me as it seems inefficient. Aren’t there CPU flags that indicate overflow and underflow?
