The Crystal Programming Language Forum

Arithmethic overflow should not raise an exception

Arithmetic overflow shouldn’t cause an exception, it should just resort to the default max value.

There’s another overflow problem

That would be really terrible! If an arithmetic value can’t be expressed in the designated type, this is a clear example of undefined state which can’t be simply recovered by assigning some default value.

2 Likes

Not having an exception, but a value returning the MAX value of an Int type is not a “undefined state”, it’s just a way of not slapping the developer in the face with an exception.

It’s not “assigning some default value”, it’s the default value of the type explicitly set by the developer…

Then how does the developer tell whether an addition overflowed or just ended up on the max value? How to tell these two states apart?

If you don’t want an exception you can use the wrapping versions of https://crystal-lang.org/reference/syntax_and_semantics/operators.html#arithmetic-operators. They wont raise, but just wrap as they did before overflow detection was implemented.

1 Like

I’m starting to think we should disallow unions of primitive numbers

1 Like

You’re mixing cause and consequence. The cause is that the result doesn’t fit in the data type. That’s an undefined state. In order to resolve that undefined state state you can take different courses of action. One is assigning an arbitrary value (like the max value of the data type). But in most general cases, raising an exception is certainly the best solution because. It disrupts normal execution because it can’t seriously continue without a way to properly express the return value.

This is actually a very strong case for a situation that requires a slap in the face. Otherwise you would maybe never discover there’s a bug in your code. An arithmetic overflow is very clearly a bug and needs to be resolved.

1 Like

There are some scenarios were saturation arithmetic operations are needed/prefered.

Overflowing (+) and modulo (&+) are more common IME, and so they have designated operators.

For saturating the LLVM intrinsics can be declared and used. https://llvm.org/docs/LangRef.html#saturation-arithmetic-intrinsics

I tried it, it’s a bit impossible to do. Discarding the idea.

1 Like

Rust has a structure for representing saturating math operations (see here). I worked a bit on what that might look like in Crystal a bit ago, but I didn’t think people had a use-case for it, so I didn’t try to make a proper shard out of it. I might do that now. An important note, though, is that it’s necessary to make numeric types saturating; that’s not a way that other languages (as far as I know) work by default, and it’s certainly not a way that normal math usually works.

Assume there is a class which has property gold = 0. This gold can increase through various ways. The developer already knows the max value will be 2147483647. However, if that ivar gold increases by just one number, there is an exception. It’s already implied that should be the max value. A max value of a number is a perfect relationship with its type.

It’s not a “undefined state” at all. It’s redundant error checking that’s forced on the developer.

So later when the client reclaims the amount of gold to the bank, the bank says “Oh, you knew from the beginning that you can only have max 2147483647 gold, right?”.

It seems like if I’m the bank, I’d like to immediately know the client is missing money so we later don’t get into trouble.

3 Likes

(though as a bank it’s very unlikely to make the jump straight from Cobol to Crystal, but…)

3 Likes

Okay, I’ll litter my codebase with if statements every time my int value increases, to protect it from an exception. Definitely seems worth it! Wait, let me create a custom method give_gold and just use this everywhere! Oh wait, now I’ll need to create a custom method give_xxx for each different ivar. Got to make sure that int value doesn’t crash my program!

If it was for a bank, your point makes sense. But we’d be using a different type and have limits set already in place as well. For a game, or a int value in hobby programming, it’s insanely annoying.

You sure must be insanely annoyed by a lot of programming languages. Seems exhausting to get upset so much, I wouldn’t want to have to trade with you!

Seems exhausting to add one line if statements everywhere to protect an int from raising an exception, yes.

class Klass
  property gold = 0
end

p = Klass.new

p.gold += Int32::MAX
p.gold += 1
pp p.gold

LOL

Is that not better then silently capping the amount of gold someone can have? Otherwise they would be putting in effort to get more gold but ever have their bank account go up anymore.

The exception makes it more clear there is a bug, and forces you to handle it.

I’m confused why you would need to do this? Is it not better to just change the type of the number if it’s possible for it to overflow? Or add in some validation of these values.

Also do remember you can use the wrapping operators, however then you could end up with negative money…

There is no bug, though. The value is simply trying to be added to another value of a type that has a MAX VALUE. If it has reached a max value, it should be set to that max value, not throw an exception.

I’d argue having the value be set to the max value is far better than an app crashing (assuming they forgot to rescue it)

Right, is this not something that should be prevented by some validation before ever reaching the point where the addition happens?

Following on the game example, I as a user spend hours and hours farming for loot to sell. I currently am near the Int32 max (unbeknownst to me). I then go to the market the sell my spoils. After selling it all I notice I only got 100k gold, when i should have got another 400k, but my gold was capped at the Int32 max, and I lost the items, and didn’t get compensated for them. How would this not be a bug?

I would expect something to happen when the user tries to sell the loot, saying “hey you reached the max amount of gold, how about sharing the wealth?”, and not allowing that transaction to go through.

Silently ignoring an issue because one did not properly validate user input and/or allowed the game to get into an unhandled state sounds like not the best idea.

Crashing an application instead of defaulting a value to its MAX VALUE is not the best idea.

Sure, that’s a good system in place! However, the system doesn’t need to raise an exception, it could just be an in-game bug, or a suggestion a player can create.