Hacker News new | ask | show | jobs
by distcs 945 days ago
A few things I've found notoriously difficult to do in C++:

1. Add two signed 64-bit integers without overflowing on either side (negative or positive) and without using 128-bit integers. If the sum is going to overflow, generate an error. If the sum is not going to overflow, evaluate the sum.

2. Multiply two signed 64-bit integers without overflowing on either side (negative or positive) and without using 128-bit integers. If the product is going to overflow, generate an error. If the product is not going to overflow, evaluate the product.

Essentially under no circumstance evaluate something that might overflow. Detect the overflow and generate error. Evaluate only if the detection algorithm says there is not going to be overflow.

We can't use 128-bit integers ecause some hardware may not have a 128-bit registers. It's probably possible to solve these but it gets complex pretty soon once you begin handling all the failure modes.

Does Rust make this kind of problems easy to be solved?

8 comments

> 1. add two signed 64 but it's without overflowing

  fn f(x: i64, y: i64) -> Option<i64> {
    x.checked_add(y)
  }
Option<T> being the Rust equivalent for std::optional, but with all the nice ADT features so it's ergonomic to use. You can transform it to a Result<T, E> if you want, or even just panic and exit if it overflows. In any case, the overflow handling is built-in and doesn't use 128 bit ints.

> 2. Multiply two signed 64 bit ints without overflow

  x.checked_mul(y)
(https://doc.rust-lang.org/std/primitive.i64.html#method.chec..., https://doc.rust-lang.org/std/primitive.i64.html#method.chec...)

  > Does Rust make this kind of problems easy to be solved?
For this specific case (specified behavior on overflow), Rust makes it trivial. All of the primitive integer types have methods such as `checked_mul()` or `saturating_add()`, which provide deterministic arithmetic on all platforms.
Rust often has convenience methods in its standard library for things like this that are common and tend to produce bugs when re-implemented over and over, and have a reasonable way to do it without making tradeoffs that will annoy some people over others.

In this case, it’s:

    10i64.checked_add(20i64)
    10i64.checked_mul(20i64)
Both expressions return an Option type, which is effectively a tagged union with type safety on top so you can’t misuse it. You can either call “unwrap” to get the value out and trigger stack unwinding on error, or pattern match on the Option and its inner value in a more functional style.
I'm pretty sure a good math library makes this problem easy to be solved. Not sure you need a new language.
If you use MSVC maybe it's more of an issue, but on GCC and Clang we have __builtin_overflow_*. There are many variants: https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins...

I use them all the time.

When you have wrappers over these that produce optional types, and use statement expression macros or monadic member functions to handle the control flow, I think it's not too hard.
Rust has i64::checked_add and i64::checked_mul, which return None on an overflow. Not only is it easy to do, the compiler help ensure you checked the result.
use some bigint libraries,or,use long double?