Начнем с простой формы с комментариями:
long saturatedAdd(long x, long y) {
long r = x + y;
// Addition is safe from overflow if x and y have different signs
if ((x < 0) != (y < 0)) {
return r;
}
// Result has overflowed if the resulting sign is different
if ((r < 0) != (x < 0)) {
return x < 0 ? Long.MIN_VALUE : Long.MAX_VALUE;
}
// Otherwise result has not overflowed
return r;
}
Хотя в использовании этой реализации нет ничего плохого, ниже следует попытка микро- «оптимизировать» ее только для аргументации.
(x < 0) != (y < 0)
может быть изменено на (x ^ y) < 0
, которое по существу является битовым XOR
знакового бита.
// Addition safe from overflow if x and y have different signs
if ((x ^ y) < 0) {
return r;
}
// Result has overflowed if resulting sign is different
if ((r ^ x) < 0) {
return x < 0 ? Long.MIN_VALUE : Long.MAX_VALUE;
}
Кроме того, мы могли бы принудительно сложить два if
, написав (x ^ y) < 0 || (r ^ x) >= 0
или даже ((x ^ y) | ~(r ^ x)) < 0
. В этот момент он перестает быть читаемым:
if (((x ^ y) | ~(r ^ x)) < 0) {
return r;
}
return x < 0 ? Long.MIN_VALUE : Long.MAX_VALUE;
Мы могли бы взять подсказку с Math.exactAdd()
и превратить if
в ((r ^ x) & (r ^ y)) < 0
. Это не улучшает читаемость, но выглядит «круто» и более симметрично:
if (((r ^ x) & (r ^ y)) < 0) {
return x < 0 ? Long.MIN_VALUE : Long.MAX_VALUE;
}
return r;
Вот так, это немного прыжок. По сути, он проверяет, имеет ли результат различный знак для обоих входов , что возможно только в том случае, если оба входа имеют одинаковый знак И результирующий знак отличается от этого .
Движение вперед, добавление 1
к Long.MAX_VALUE
приводит к Long.MIN_VALUE
:
if (((r ^ x) & (r ^ y)) < 0) {
return Long.MAX_VALUE + (x < 0 ? 1 : 0);
}
return r;
Другой способ получить единицу, когда x < 0
, - использовать этот знаковый бит как единое целое.
if (((r ^ x) & (r ^ y)) < 0) {
return Long.MAX_VALUE + (x >>> (Long.SIZE - 1));
}
Наконец, просто ради симметрии, измените на r
вместо этого x
, чтобы получить:
long r = x + y;
if (((r ^ x) & (r ^ y)) < 0) {
return Long.MIN_VALUE - (r >>> (Long.SIZE - 1));
}
return r;