This proposal is more for the sake of completeness rather than something that should be added to Zig any time soon.
Zig has explicit support for integer operations with fault on overflow, and wrap-around behaviour. But there is a third relatively common behaviour for integer operations, especially in DSPs: saturating arithmetic: https://en.wikipedia.org/wiki/Saturation_arithmetic
Here's some related proposals from LLVM:
https://reviews.llvm.org/D6976
https://reviews.llvm.org/D8371
There are some options how to add support for this:
I don't have any opinion on how to implement it. This is just a basis for discussion, and I think Zig needs the input from someone with good experience with DSPs to get feedback on that use-case before this proposal is considered.
where is such behaviour used/ wanted?
Seems there are instructions in several architectures with saturating behavior, including ARM and SIMD. Makes sense to me that we should explicitly declare that behavior if we want it, rather than rely on the compiler to guess intent. Maybe it isn't enough of a common case to expend operators on and we should just use builtins (@saturateAdd(), etc), but it would be more consistent. To that end, I propose <op>|, for the simple reason that it looks like a wall.
a +| b;
a +|= b;
a -| b;
a -|= b;
a *| b;
a *|= b;
@monouser7dig Basically any kind of signal processing math code. I guess what most people may be familiar is image processing. Imagine you want to double the brightness, so you multiply each value by 2. But you don't want the brightness to wrap around, so when it goes above 255, you clamp it to 255. The Wikipedia article explained it fairly well.
@tgschultz Yeah, that seems to be the most visually descriptive. But it could be confusing that | is used with bitwise or as well.
That鈥檚 a nice example, I think builtins for these operations would be a good addition.
Wouldn't @clampedAdd make more intuitive sense, considering that you'd implement this functionality with a call to some clamp function?
It's not like we want people to have to read Wikipedia articles to understand language features, right?
Note that LLVM has built-in support for saturating add and subtract: https://llvm.org/docs/LangRef.html#saturation-arithmetic-intrinsics
I've found them useful on a number of occasions in my Rust code, where saturating_add and saturating_sub methods are defined on all supported integer types. And having them built-in to the language means LLVM can properly reason-about and optimize the code.
I don't know if the status quo names are final, but the current naming convention is something like @opnameWithUniqueBehaviour, so perhaps:
@addWithClamping(a, b)
@subWithClamping(a, b)
@mulWithClamping(a, b)
@shlWithClamping(a, b)
However, afaik we don't have a @clamp operation, so this feels a little out of place. @clamp operation would also take a min and max parameter, which this would not, so perhaps a separate word is in order, even if it does require checking documentation.
@addWithSaturation(a, b)
@subWithSaturation(a, b)
@mulWithSaturation(a, b)
@shlWithSaturation(a, b)
Most helpful comment
Seems there are instructions in several architectures with saturating behavior, including ARM and SIMD. Makes sense to me that we should explicitly declare that behavior if we want it, rather than rely on the compiler to guess intent. Maybe it isn't enough of a common case to expend operators on and we should just use builtins (@saturateAdd(), etc), but it would be more consistent. To that end, I propose
<op>|, for the simple reason that it looks like a wall.