adicionando “int” com “uint_64t”
Pergunta
Existem três variáveis com os seguintes tipos
uint64_t old_addr, new_addr;
int delta;
e eu quero fazer esta tarefa
new_addr = old_addr + delta;
Contudo, o problema é que, quando old_addr=915256
e delta=-6472064
, o new_addr
torna-se 18446744069414584325
para consertar isso tenho que verificar algumas coisas:
if ( delta < 0 ) {
if ( old_addr < abs(delta) )
new_addr = 0;
else
new_addr = old_addr + delta;
}
Existe uma maneira melhor e eficiente?
Solução
A questão é quais valores old_addr
e new_addr
pode levar.E por que eles são uint64_t
, em vez de simplesmente int
.A expressão mais simples seria:
new_addr = old_addr + std::min( delta, -static_cast<int>( old_addr ) );
, mas se old_addr
pode ser maior que INT_MAX
, isso não funcionará.Caso contrário, as regras da aritmética assinada/não assinada mista em C/C ++ são tais que você provavelmente é mais seguro usando explícito if
s, e não arriscar nenhuma aritmética mista antes de ter certeza dos valores.
E observe que na maioria das máquinas, abs( delta )
ainda será negativo sedelta
é igual a INT_MIN
.Para lidar corretamente a todos os casos, você precisará de algo como:
if ( delta > 0 ) {
new_addr = std::numeric_limits<uin64_t>::max() - delta > old_addr
? old_addr + delta
: std::numeric_limits<uint64_t>::max();
} else if ( delta < 0 ) {
new_addr = old_addr != 0 && -(delta + 1) < old_addr - 1
? old_addr + delta
: 0;
} else {
new_addr = old_addr;
}
(Apenas fora do topo da minha cabeça.Poderia facilmente haver um erro por um erro.)
Outras dicas
Isso é chamado de adição saturada, e alguns processadores possuem instruções de máquina especiais para isso.Você pode extrair esse código em uma função embutida e, dependendo do ambiente de execução de destino, usar a instrução de máquina.
Em vez de abs(delta)
você pode simplesmente escrever -delta
, já que você já sabe disso delta < 0
.
Este código é muito simples e cuida do overflow nas duas direções.
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
static uint64_t saturated_add(uint64_t a, int delta) {
uint64_t result = a + delta;
if (delta < 0 && result > a) {
return 0;
} else if (delta > 0 && result < a) {
return -1;
} else {
return result;
}
}
int main() {
assert(saturated_add(915256, -6472064) == 0);
assert(saturated_add(100, -99) == 1);
assert(saturated_add(100, -100) == 0);
assert(saturated_add(100, -101) == 0);
assert(saturated_add(UINT64_C(0x1111222233334444), -0x33334445) == UINT64_C(0x11112221FFFFFFFF));
assert(saturated_add(-5, 6) == UINT64_C(-1));
assert(saturated_add(-5, 5) == UINT64_C(-1));
assert(saturated_add(-5, 4) == UINT64_C(-1));
assert(saturated_add(-5, 3) == UINT64_C(-2));
return 0;
}