Saturating, what does it really do and when is it useful?

I found out about family of saturating functions and tried some explanation on the tinter but couldn't really grasp what is it for and when is it usefull. Rust documentation to the best of my knowledge is really "robotic" here.

Could anyone please explain (with examples) what is it for?
Thank you

Integers have a finite range (0 to 2ⁿ-1), depending on the number of bits n they use:
u8: 0 - 2⁸-1 (255)
u16: 0 - 2¹⁶-1 (65535)

So when you add/subtract/multiply two values and the result does not fit, something else has to happen.
The options are:

  • wrap around 2ⁿ (200u8.wrapping_add(100u8) -> 44u8 (300 modulo 256)
  • saturate at 2ⁿ-1 for addition and 0 for subtraction (200u8.saturating_add(100u8) -> 255u8)
  • checked: panic

OK, so if I understand correctly, saturating when adding will give back max value for that type, am I correct?

It will give you the valid value closest to the correct value. For addition with results over the max value that is the max value.
For subtraction with results below the minimum value, it is the minimum value (0 for unsigned and 2^(n-1) for signed).

4 Likes

So basically it will clamp the value to the max of that type.

These are some use-cases for saturating operations, many found by searching in real Rust code.

Make the player lose 10 coins when they die, avoiding overflow:

player.coins = player.coins.saturating_sub(10);

Align some text to the middle, but if it is too large align it to the left edge:

let position = (total_width/2).saturating_sub(text_width/2);

Parse a digit into a number n, capping off too large numbers such that "8354" will be cut off at 255:

n = n.saturating_mul(10_u8).saturating_add(digit);

Move the cursor to the left unless it is at the left edge:

cursor.position.x = cursor.position.x.saturating_sub(1);
3 Likes

Correct for addition.
It clamps it to the valid range.

1 Like

Also correct for subtraction.

10 - 20 results in zero.

A typical use case for this is when combining two images by adding all their corresponding pixel values together. One would rather that the total amount of red, say, in the resulting pixels does not exceed 255 for a 8 bit color values. Otherwise the combined value might overflow and wrap around to something small, which is not the effect one wants.

I suspect "max of that type" is a bit vague.
It can be read as "limits of the type" (0 - 2^n-1), or "maximum possible value (2^n-1)".

Seriously, what is vague about it? It is the most precise description literally. Min and Max values of that type. Cannot see it being more precise.

That was what I assumed, but that would make it incorrect for subtraction which clamps to the min value.

The following makes more sense for limits:

Checking the rust docs for saturatomh_add, https://doc.rust-lang.org/std/primitive.u32.html#method.saturating_add, I see it described as:

pub const fn saturating_add(self, rhs: u32) -> u32

Saturating integer addition. Computes self + rhs , saturating at the numeric bounds instead of overflowing.

I'm wondering how that could be made any clearer. When one does a saturating_add of a negative number one is doing a saturating subtraction and of course the lower numeric bound (zero) is where it stops.

I guess it's because the definition is recursive... "saturating integer addition... will saturate at the numeric bounds".

So it only makes sense when you already know that "saturating" means it'll clamp the resulting value to the range of values valid for that type (e.g. [0, 255] for u8).

1 Like

It's also not the case that saturating subtraction yields the minimum value. The only correct explanation so far is that of @s3bk in this post:

In addition, if you subtract a negative value and you get something too big, the result will be the maximal value of the type, and by symmetry, if you add a negative value and you get something too small, saturation will yield 0 for unsigned or -2^(n-1) for signed types.

2 Likes

Maybe. Is the job of such a document to give definitions for common words and phrases?

Looking in the dictionary I see saturate defined as:

to fill completely with something that permeates or pervades

to load to capacity

Which pretty much describes the situation in layman speak, before getting all mathematically technical about it.

1 Like