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?

Foi útil?

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 ifs, 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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top