# SafeInt - Safe 54-bit signed integer

**URL:** <https://discourse.elm-lang.org/t/safeint-safe-54-bit-signed-integer/5902>\
**Category:** Show and Tell\
**Created:** [June 8, 2020, 11:14am UTC](https://discourse.elm-lang.org/t/safeint-safe-54-bit-signed-integer/5902 "2020-06-08T11:14:57Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![malaire](https://avatars.discourse-cdn.com/v4/letter/m/b782af/32.png) [@malaire](https://discourse.elm-lang.org/u/malaire)\
**Post date:** [June 8, 2020, 11:14am UTC](https://discourse.elm-lang.org/t/safeint-safe-54-bit-signed-integer/5902/1 "2020-06-08T11:14:58Z")

</div>

I’ve just published my first Elm package: [malaire/elm-safe-int](https://package.elm-lang.org/packages/malaire/elm-safe-int/latest/)

# SafeInt

`SafeInt` is 54-bit signed integer which works for full JavaScript safe integer range from `- (2^53 - 1)` to `2^53 - 1` (from `-9007199254740991` to `9007199254740991`).

```auto
-- `2^40 / 10`
SafeInt.pow SafeInt.two (SafeInt.new 40)
    |> SafeInt.divBy (SafeInt.new 10)
    |> SafeInt.toInt
    --> Just 109951162777

```

`SafeInt` has a concept of `undefined`: When function result can’t be represented using an integer within safe integer range, result will be `undefined` which is propagated through all functions:

```auto
-- `12 / 0 * 34`
-- undefined because of division by zero
SafeInt.div (SafeInt.new 12) SafeInt.zero
    |> SafeInt.mul (SafeInt.new 34)
    |> SafeInt.toInt
    --> Nothing

-- `2 ^ 55 / 1000`
-- undefined because safe integer range is exceeded
SafeInt.pow SafeInt.two (SafeInt.new 55)
    |> SafeInt.divBy (SafeInt.new 1000)
    |> SafeInt.toInt
    --> Nothing

```

# SafeInt.Unchecked

For cases where more speed is required at the cost of some lost safety and features, module `SafeInt.Unchecked` provides `Unchecked` versions of `SafeInt` functions which work directly on `Float` values.

These are still integer functions and e.g. `divBy` does integer division:

```auto
import SafeInt.Unchecked as Unchecked

-- `2^40 / 10`
Unchecked.pow 2 40
    |> Unchecked.divBy 10
    --> 109951162777

```

# Performance

I’m not interested in doing proper benchmarking, but in some quick benchmarks:

- `SafeInt` was up to 6 times slower than `Int` or `Float`
- `Unchecked` was up to 3 times slower than `Int` or `Float`
- `SafeInt` was up to 2.5 times slower than `Unchecked`

# Too many divisions

Ever wanted a package with 24\* basic integer-division-related functions? Probably not, but here you go. 😁

```auto
import SafeInt.Unchecked as Unchecked

Unchecked.divMod -1234 100
    --> ( -13, 66 )

-1234 |> Unchecked.quotRemBy 100
    --> ( -12, -34 )

```

\*) `div`, `mod`, `quotient`, `remainder`, `divMod`, `quotRem`, then `By` versions and `SafeInt` / `Unchecked` versions.

Note: Using `flip` or even defining a function as `modBy a b = mod b a` adds significant overhead on small functions like these.

# When Int is not an integer

Did you know that in Elm `Int` can be `NaN`, `-Infinity`, `+Infinity`, or even a floating-point value like `1234.5` ?

But don’t worry, `SafeInt` handles all of these cases - it wouldn’t be **Safe** if it didn’t. For example `SafeInt.fromInt : Int -> SafeInt` is described as “Convert `Int` to `SafeInt`, rounding towards zero”, so if you give it a floating-point value as `Int`, it will be properly truncated to an integer. And `NaN` or infinities will be converted to `undefined`.

`SafeInt.pow` also truncates the result, always returning either an integer or `undefined`, unlike `^` operator:

```auto
$ elm repl
> round 2 ^ -1
0.5 : Int
> import SafeInt
> SafeInt.pow SafeInt.two (SafeInt.new -1)
Defined 0 : SafeInt.SafeInt

```

And `SafeInt` functions of course never crash, unlike `Int` functions.

More about design goals in README.

---

<div class="post-metadata">

**Author:** ![rupert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rupert/32/1775_2.png) [@rupert](https://discourse.elm-lang.org/u/rupert)\
**Post date:** [June 8, 2020, 11:35am UTC](https://discourse.elm-lang.org/t/safeint-safe-54-bit-signed-integer/5902/2 "2020-06-08T11:35:23Z")

</div>

Very nice. Do you think an identical API is possible with safe _64-bit_ integers?

Reason I ask is I’ve worked with a few back-end APIs recently that provided values that could take a full 64-bit range. These were not just identifiers, they were scalar values.

---

<div class="post-metadata">

**Author:** ![malaire](https://avatars.discourse-cdn.com/v4/letter/m/b782af/32.png) [@malaire](https://discourse.elm-lang.org/u/malaire)\
**Post date:** [June 8, 2020, 11:47am UTC](https://discourse.elm-lang.org/t/safeint-safe-54-bit-signed-integer/5902/3 "2020-06-08T11:47:48Z")

</div>

There is [folkertdev/elm-int64](https://package.elm-lang.org/packages/folkertdev/elm-int64/latest/) for some 64-bit operations, but it doesn’t implement division or multiplication which are tricky to get correct.

To implement e.g. 64-bit multiplication, I think it would be necessary to split 64-bit integer into three parts each at most 26 or 27 bits, as then result of each individual multiplication would fit within safe 54 bit range.

Calculations would include quite many individual steps, and would be much slower than with 54-bit integer which is basically just a single `Float` operation (with many additional checks in case of `SafeInt`).

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [June 18, 2020, 11:47am UTC](https://discourse.elm-lang.org/t/safeint-safe-54-bit-signed-integer/5902/4 "2020-06-18T11:47:53Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
