I’ve just published my first Elm package: malaire/elm-safe-int
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
).
-- `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:
-- `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:
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 thanInt
orFloat
-
Unchecked
was up to 3 times slower thanInt
orFloat
-
SafeInt
was up to 2.5 times slower thanUnchecked
Too many divisions
Ever wanted a package with 24* basic integer-division-related functions? Probably not, but here you go.
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:
$ 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.