Eu estava escrevendo um programa em C ++ para encontrar todas as soluções de um b = c , onde a , b e c juntos usar todos os dígitos 0-9 exatamente uma vez. O programa em loop sobre os valores de um e b , e funcionou uma rotina dígitos de contagem de cada vez em um , b e um b para verificar se a condição dígitos estava satisfeito.
No entanto, soluções falsas podem ser geradas quando a b excede o limite inteiro. Acabei verificando isso usando código como:
unsigned long b, c, c_test;
...
c_test=c*b; // Possible overflow
if (c_test/b != c) {/* There has been an overflow*/}
else c=c_test; // No overflow
Existe uma maneira melhor de testar o estouro? Eu sei que alguns chips têm um sinalizador interno que é definido quando ocorre um estouro, mas nunca o vi acessado por C ou C ++.
Cuidado que o estouro assinado int
é um comportamento indefinido em C e C ++ e, portanto, você deve detectá-lo sem realmente causar isso. Para estouro int assinado antes da adição, consulte Detectando estouro assinado em C / C ++ .
fonte
-ftrapv
fará com que ele gere um SIGABRT no estouro de número inteiro (assinado). Veja aqui .clz
instrução ou a__clz(unsigned)
função para determinar a classificação do número (onde está o seu bit mais alto). Como não tenho certeza se isso está disponível no x86 ou no x64, assumirei que não e dirá que encontrar o bit mais significativo precisará das pioreslog(sizeof(int)*8)
instruções.Respostas:
Vejo que você está usando números inteiros não assinados. Por definição, em C (não sei sobre C ++), a aritmética não assinada não transborda ... então, pelo menos para C, seu argumento é discutível :)
Com números inteiros assinados, após o estouro, ocorre um comportamento indefinido (UB) e seu programa pode fazer qualquer coisa (por exemplo: tornar os testes inconclusivos).
Para criar um programa em conformidade, você precisa testar o estouro antes de gerar o referido estouro. O método também pode ser usado com números inteiros não assinados:
Para a divisão (exceto para o
INT_MIN
e-1
caso especial), não há qualquer possibilidade de ir maisINT_MIN
ouINT_MAX
.fonte
x >= 0
-x > 0
será suficiente (sex == 0
, entãox + a
não pode transbordar por razões óbvias).if ((a < INT_MIN / x))
teste é tarde demais. Umif (x == -1)
teste é necessário primeiro.Não é uma maneira de determinar se uma operação é susceptível de excesso, usando as posições dos one-bits mais significativos nos operandos e um pouco de conhecimento básico binário matemática.
Além disso, quaisquer dois operandos resultarão em (no máximo) um pouco mais que o bit mais alto do maior operando. Por exemplo:
Para multiplicação, quaisquer dois operandos resultarão (no máximo) na soma dos bits dos operandos. Por exemplo:
Da mesma forma, você pode estimar o tamanho máximo do resultado da
a
potência dab
seguinte forma:(Substitua o número de bits pelo número inteiro de destino, é claro.)
Não tenho certeza da maneira mais rápida de determinar a posição do bit mais alto de um número, aqui está um método de força bruta:
Não é perfeito, mas isso lhe dará uma boa idéia se dois números podem estourar antes de você fazer a operação. Não sei se seria mais rápido do que simplesmente verificar o resultado da maneira que você sugeriu, por causa do loop na
highestOneBitPosition
função, mas poderia ser (especialmente se você soubesse quantos bits havia nos operandos anteriormente).fonte
log2
, mas isso não seria necessariamente tão óbvio para alguém que não possuía formação matemática.multiplication_is_safe
0x8000 * 0x10000
overflow (as posições dos bits são 16 + 17 = 33, que é > 32 ), embora isso não ocorra porque o0x8000 * 0x10000 = 0x80000000
que obviamente ainda se encaixa em um int de 32 bits não assinado. Este é apenas um dos exemplos de maio para os quais esses códigos não funcionam.0x8000 * 0x10001
, ...0x8000 * 0x10000
não é "seguro", por esta definição, mesmo que tudo esteja bem.O Clang 3.4+ e o GCC 5+ oferecem embutidos aritméticos verificados. Eles oferecem uma solução muito rápida para esse problema, especialmente quando comparados às verificações de segurança de teste de bits.
Para o exemplo da pergunta do OP, funcionaria assim:
A documentação do Clang não especifica se
c_test
contém o resultado do estouro se ocorreu um estouro, mas a documentação do GCC diz que sim. Dado que esses dois gostam de ser__builtin
compatíveis, provavelmente é seguro assumir que é assim que Clang também funciona.Existe um
__builtin
para cada operação aritmética que pode exceder (adição, subtração, multiplicação), com variantes assinadas e não assinadas, para tamanhos int, tamanhos longos e tamanhos longos. A sintaxe para o nome é__builtin_[us](operation)(l?l?)_overflow
:u
para sem sinal ous
para assinado ;add
,sub
oumul
;l
sufixo significa que os operandos sãoint
s; uml
significalong
; doisl
s significalong long
.Portanto, para uma adição inteira comprada assinada marcada, seria
__builtin_saddl_overflow
. A lista completa pode ser encontrada na página de documentação do Clang .GCC 5+ e Clang 3.8+ além de oferecer builtins genéricos que funcionam sem especificar o tipo dos valores:
__builtin_add_overflow
,__builtin_sub_overflow
e__builtin_mul_overflow
. Eles também funcionam em tipos menores queint
.Os componentes internos são inferiores ao que é melhor para a plataforma. No x86, eles verificam os sinalizadores de transporte, transbordamento e sinalização.
O cl.exe do Visual Studio não possui equivalentes diretos. Para adições e subtrações não assinadas, inclusive
<intrin.h>
permitirá que você useaddcarry_uNN
esubborrow_uNN
(onde NN é o número de bits, comoaddcarry_u8
ousubborrow_u64
). A assinatura deles é um pouco obtusa:c_in
/b_in
é o sinalizador carry / emprestado na entrada e o valor de retorno é o carry / emprestado na saída. Não parece ter equivalentes para operações ou multiplicações assinadas.Caso contrário, o Clang for Windows agora está pronto para produção (bom o suficiente para o Chrome), então também pode ser uma opção.
fonte
__builtin_sub_overflow
definitivamente não está no Clang 3.4.__builtin_add_overflow
e os amigos já devem estar disponíveis no Clang 3.8.Alguns compiladores fornecem acesso ao sinalizador de estouro de número inteiro na CPU que você pode testar, mas isso não é padrão.
Você também pode testar a possibilidade de estouro antes de executar a multiplicação:
fonte
b == ULONG_MAX / a
? Então ainda pode caber, dado quea
divideULONG_MAX
sem resíduos.Aviso: o GCC pode otimizar uma verificação de estouro ao compilar com
-O2
. A opção-Wall
emitirá um aviso em alguns casos, comomas não neste exemplo:
A única maneira segura é verificar o estouro antes que ocorra, conforme descrito no documento CERT , e isso seria incrivelmente tedioso para ser usado sistematicamente.
Compilar com
-fwrapv
resolve o problema, mas desativa algumas otimizações.Precisamos desesperadamente de uma solução melhor. Eu acho que o compilador deve emitir um aviso por padrão ao fazer uma otimização que depende do estouro não ocorrer. A situação atual permite que o compilador otimize uma verificação de estouro, o que é inaceitável na minha opinião.
fonte
for(int k = 0; k < 5; k++) {...}
deveria dar um aviso?k
podem ser facilmente determinados em tempo de compilação. O compilador não precisa fazer nenhuma suposição.n
é inferior a 32, antes de emitir uma instrução shift que usa apenas os 5 bits inferiores den
?O Clang agora suporta verificações dinâmicas de estouro para números inteiros assinados e não assinados. Veja a opção -fsanitize = integer . Por enquanto, é o único compilador C ++ com verificação de estouro dinâmico totalmente suportada para fins de depuração.
fonte
Vejo que muitas pessoas responderam à pergunta sobre estouro, mas eu queria resolver o problema original. Ele disse que o problema era encontrar um b = c de modo que todos os dígitos sejam usados sem repetição. Ok, não foi isso que ele pediu neste post, mas ainda acho que era necessário estudar o limite superior do problema e concluir que ele nunca precisaria calcular ou detectar um estouro (nota: não sou proficiente em matemática, eu fiz isso passo a passo, mas o resultado final foi tão simples que isso pode ter uma fórmula simples).
O ponto principal é que o limite superior exigido pelo problema para a, b ou c é 98.765.432. De qualquer forma, começando dividindo o problema nas partes trivial e não trivial:
Agora, apenas precisamos mostrar que nenhuma outra solução é possível e apenas as permutações são válidas (e o código para imprimi-las é trivial). Voltamos ao limite superior. Na verdade, o limite superior é c ≤ 98.765.432. É o limite superior porque é o maior número com 8 dígitos (total de 10 dígitos menos 1 para cada aeb). Esse limite superior é apenas para c porque os limites de aeb devem ser muito mais baixos devido ao crescimento exponencial, como podemos calcular, variando de b de 2 ao limite superior:
Observe, por exemplo, a última linha: diz que 1,97 ^ 27 ~ 98M. Assim, por exemplo, 1 ^ 27 == 1 e 2 ^ 27 == 134.217.728 e essa não é uma solução, pois possui 9 dígitos (2> 1,97; portanto, é realmente maior do que deveria ser testado). Como pode ser visto, as combinações disponíveis para testar aeb são realmente pequenas. Para b == 14, precisamos tentar 2 e 3. Para b == 3, começamos em 2 e paramos em 462. Todos os resultados são concedidos como inferiores a ~ 98M.
Agora basta testar todas as combinações acima e procurar as que não repetem nenhum dígito:
Nenhum deles corresponde ao problema (que também pode ser visto pela ausência de '0', '1', ..., '9').
O código de exemplo que resolve ele segue. Observe também que está escrito em Python, não porque ele precisa de números inteiros de precisão arbitrários (o código não calcula nada maior que 98 milhões), mas porque descobrimos que a quantidade de testes é tão pequena que devemos usar uma linguagem de alto nível para faça uso de seus contêineres e bibliotecas integrados (observe também: o código possui 28 linhas).
fonte
Aqui está uma solução "não portátil" para a pergunta. As CPUs Intel x86 e x64 possuem o chamado registro EFLAGS , que é preenchido pelo processador após cada operação aritmética inteira. Vou pular uma descrição detalhada aqui. Os sinalizadores relevantes são o sinalizador "Estouro" (máscara 0x800) e o sinalizador "Transporte" (máscara 0x1). Para interpretá-los corretamente, deve-se considerar se os operandos são do tipo assinado ou não assinado.
Aqui está uma maneira prática de verificar os sinalizadores do C / C ++. O código a seguir funcionará no Visual Studio 2005 ou mais recente (32 e 64 bits), bem como no GNU C / C ++ 64 bits.
Se os operandos fossem multiplicados sem transbordamento, você obteria um valor de retorno 0 de
query_intel_eflags(0x801)
, ou seja, nem os sinalizadores de transporte nem de transbordamento são definidos. No código de exemplo fornecido de main (), ocorre um estouro e os dois sinalizadores são definidos como 1. Essa verificação não implica cálculos adicionais; portanto, deve ser bastante rápido.fonte
Se você tiver um tipo de dados maior que o que você deseja testar (digamos que você adicione um de 32 bits e um de 64 bits), isso detectará se ocorreu um estouro. Meu exemplo é para uma adição de 8 bits. Mas pode ser ampliado.
É baseado nos conceitos explicados nesta página: http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Comb/overflow.html
Para um exemplo de 32 bits,
0xFF
torna0xFFFFFFFF
-0x80
se0x80000000
e torna - se e, finalmente,uint16_t
torna-se umuint64_t
.NOTA : isso captura estouros de adição / subtração de números inteiros e percebi que sua pergunta envolve multiplicação. Nesse caso, a divisão é provavelmente a melhor abordagem. Geralmente, é uma maneira de as
calloc
implementações garantirem que os parâmetros não excedam à medida que são multiplicados para obter o tamanho final.fonte
A maneira mais simples é converter seus
unsigned long
s emunsigned long long
s, fazer sua multiplicação e comparar o resultado com 0x100000000LL.Você provavelmente descobrirá que isso é mais eficiente do que fazer a divisão, como você fez no seu exemplo.
Ah, e funcionará em C e C ++ (como você marcou a pergunta com ambos).
Acabei de dar uma olhada no manual da glibc . Há uma menção de uma interceptação de excesso de número inteiro (
FPE_INTOVF_TRAP
) como parte deSIGFPE
. Isso seria ideal, além dos bits desagradáveis no manual:Um pouco de pena mesmo.
fonte
ULONG_MAX
que é mais fácil de digitar e mais portátil que a codificação0x100000000
.long
elong long
é do mesmo tamanho (por exemplo, em muitos compiladores de 64 bits).Aqui está uma maneira muito rápida de detectar o estouro de pelo menos adições, o que pode dar uma vantagem para multiplicação, divisão e potência.
A idéia é que, exatamente porque o processador deixará o valor voltar a zero e que o C / C ++ seja abstraído de qualquer processador específico, você pode:
Isso garante que, se um operando é zero e outro não, o estouro não será falsamente detectado e será significativamente mais rápido do que muitas operações de teste NOT / XOR / AND /, conforme sugerido anteriormente.
Como apontado, essa abordagem, embora melhor do que outras formas mais elaboradas, ainda é otimizável. A seguir, é apresentada uma revisão do código original que contém a otimização:
Uma maneira mais eficiente e barata de detectar o estouro de multiplicação é:
Isso resulta em UINT32_MAX no estouro ou no resultado da multiplicação. É um comportamento estritamente indefinido para permitir que a multiplicação prossiga para números inteiros assinados nesse caso.
fonte
x+y>=256
evalue=x+y-256
. Comoy<256
sempre é verdade, (y-256) é negativo evalue < x
sempre é verdade. A prova para o caso não transbordante é bastante semelhante.uint32_t x[N], y[N], z[N], carry=0; for (int i = 0; i < N; i++) { z[i] = x[i] + y[i] + carry; carry = z[i] < (x[i] | y[i]); }
Se você não fizeror
os valores, não poderá distinguir entre um operando e o bit de transporte sendo zero e um bit de operando0xffffffff
e o bit de transporte sendo um.Você não pode acessar o sinalizador de estouro de C / C ++.
Alguns compiladores permitem inserir instruções de interceptação no código. Em GCC a opção é
-ftrapv
.A única coisa portátil e independente do compilador que você pode fazer é verificar os estouros por conta própria. Assim como você fez no seu exemplo.
No entanto,
-ftrapv
parece não fazer nada no x86 usando o GCC mais recente. Eu acho que é uma sobra de uma versão antiga ou específica para alguma outra arquitetura. Eu esperava que o compilador inserisse um código de operação INTO após cada adição. Infelizmente não faz isso.fonte
Para números inteiros não assinados, verifique se o resultado é menor que um dos argumentos:
Para números inteiros assinados, você pode verificar os sinais dos argumentos e do resultado.
Os números inteiros de sinais diferentes não podem transbordar e os números inteiros do mesmo sinal transbordam apenas se o resultado for de um sinal diferente:
fonte
char result = (char)127 + (char)3;
seria -126; menor que os dois operandos.Eu precisava responder a mesma pergunta para números de ponto flutuante, onde mascaramento e mudança de bits não parecem promissores. A abordagem que decidi trabalhar para números assinados e não assinados, números inteiros e de ponto flutuante. Funciona mesmo se não houver um tipo de dados maior a ser promovido para cálculos intermediários. Não é o mais eficiente para todos esses tipos, mas como funciona para todos eles, vale a pena usá-lo.
Teste, adição e subtração de estouro assinado:
Obtenha as constantes que representam os maiores e menores valores possíveis para o tipo, MAXVALUE e MINVALUE.
Calcule e compare os sinais dos operandos.
uma. Se qualquer valor for zero, nem a adição nem a subtração podem exceder. Pule os testes restantes.
b. Se os sinais são opostos, a adição não pode transbordar. Pule os testes restantes.
c. Se os sinais forem os mesmos, a subtração não poderá transbordar. Pule os testes restantes.
Teste para estouro positivo de MAXVALUE.
uma. Se ambos os sinais forem positivos e MAXVALUE - A <B, a adição transbordará.
b. Se o sinal de B for negativo e MAXVALUE - A <-B, a subtração transbordará.
Teste para estouro negativo de MINVALUE.
uma. Se ambos os sinais são negativos e MINVALUE - A> B, a adição transbordará.
b. Se o sinal de A for negativo e MINVALUE - A> B, a subtração transbordará.
Caso contrário, não haverá transbordamento.
Teste de estouro assinado, multiplicação e divisão:
Obtenha as constantes que representam os maiores e menores valores possíveis para o tipo, MAXVALUE e MINVALUE.
Calcule e compare as magnitudes (valores absolutos) dos operandos com um. (Abaixo, suponha que A e B sejam essas magnitudes, não os originais assinados.)
uma. Se qualquer valor for zero, a multiplicação não poderá exceder e a divisão produzirá zero ou um infinito.
b. Se um dos valores for um, a multiplicação e a divisão não poderão exceder.
c. Se a magnitude de um operando estiver abaixo de um e do outro for maior que um, a multiplicação não poderá exceder.
d. Se as magnitudes são menores que uma, a divisão não pode transbordar.
Teste para estouro positivo de MAXVALUE.
uma. Se os dois operandos forem maiores que um e MAXVALUE / A <B, a multiplicação será excedida.
b. Se B for menor que um e MAXVALUE * B <A, a divisão transbordará.
Caso contrário, não haverá transbordamento.
Nota: O estouro mínimo de MINVALUE é tratado por 3, porque assumimos valores absolutos. No entanto, se ABS (MINVALUE)> MAXVALUE, teremos alguns falsos positivos raros.
Os testes para subfluxo são semelhantes, mas envolvem o EPSILON (o menor número positivo maior que zero).
fonte
1.0e-200 / 1.0e200
seria um exemplo de um underflow real, assumindo que o IEEE dobra. O termo correto aqui, em vez disso, é estouro negativo </ pedante>.1/INT_MAX
pode ser considerado com subfluxo, mas a linguagem simplesmente exige que o truncamento seja zero.A CERT desenvolveu uma nova abordagem para detectar e relatar estouro de número inteiro assinado, agrupamento de número inteiro não assinado e truncamento de número inteiro usando o modelo de número inteiro "como se" infinitamente variado (AIR). O CERT publicou um relatório técnico descrevendo o modelo e produziu um protótipo funcional com base no GCC 4.4.0 e GCC 4.5.0.
O modelo inteiro do AIR produz um valor equivalente a um que seria obtido usando números inteiros infinitamente variados ou resulta em uma violação de restrição de tempo de execução. Diferentemente dos modelos inteiros anteriores, os inteiros do AIR não exigem traps precisos e, consequentemente, não interrompem ou inibem a maioria das otimizações existentes.
fonte
Outra ferramenta interessante é o IOC: um verificador de estouro inteiro para C / C ++ .
Este é um Clang remendado compilador , que adiciona verificações ao código no momento da compilação.
Você obtém saída assim:
fonte
Outra variante de uma solução, usando linguagem assembly, é um procedimento externo. Este exemplo para multiplicação de números inteiros não assinados usando g ++ e fasm no Linux x64.
Este procedimento multiplica dois argumentos inteiros não assinados (32 bits) (de acordo com a especificação para amd64 (seção 3.2.3 Passagem de parâmetro ).
(edi e esi registra no meu código)) e retorna o resultado ou 0 se um estouro ocorreu.
Teste:
Vincule o programa ao arquivo de objeto asm. No meu caso, no Qt Creator , adicione-o
LIBS
em um arquivo .pro.fonte
Calcule os resultados com dobras. Eles têm 15 dígitos significativos. Sua exigência tem um limite superior rígido em c de 10 8 - ele pode ter no máximo 8 dígitos. Portanto, o resultado será preciso se estiver dentro do alcance e, caso contrário, não excederá o limite.
fonte
Experimente esta macro para testar o bit de estouro de máquinas de 32 bits (adaptou a solução de Angel Sinigersky)
Eu o defini como uma macro porque, caso contrário, o bit de estouro seria substituído.
A seguir é uma pequena aplicação com o segmento de código acima:
fonte
_MSC_VER
embora as compilações da Microsoft rejeitem o código).A captura de estouro de número inteiro em C indica uma solução mais geral do que a discutida pelo CERT (é mais geral em termos de tipos manipulados), mesmo que exija algumas extensões do GCC (não sei o quão amplamente elas são suportadas).
fonte
Eu não concordo com isso. Você pode escrever alguma linguagem assembly embutida e usar um
jo
instrução (jump overflow) assumindo que você esteja no x86 para interceptar o estouro. Obviamente, seu código não seria mais portátil para outras arquiteturas.Olhe para
info as
einfo gcc
.fonte
Para expandir a resposta do Head Geek, existe uma maneira mais rápida de fazer o
addition_is_safe
;Isso usa arquitetura segura da máquina, pois números inteiros não assinados de 64 e 32 bits ainda funcionarão bem. Basicamente, eu crio uma máscara que mascarará tudo, exceto o bit mais significativo. Então, mascarei os dois números inteiros e, se um deles não tiver esse bit definido, a adição será segura.
Isso seria ainda mais rápido se você pré-inicializar a máscara em algum construtor, pois ela nunca muda.
fonte
UINT_MAX + 1
. Após o mascaramento,a
o bit alto será definido, mas1
se tornará zero e, portanto, a função retornarátrue
, a adição é segura - mas você está diretamente direcionado para o estouro.mozilla::CheckedInt<T>
fornece matemática de número inteiro verificado pelo estouro para o tipo inteiroT
(usando intrínsecas do compilador no clang e no gcc, conforme disponível). O código está no MPL 2.0 e depende de três (IntegerTypeTraits.h
,Attributes.h
eCompiler.h
) outros cabeçalhos de biblioteca não padrão apenas de cabeçalho, além de mecanismos de asserção específicos para Mozilla . Você provavelmente deseja substituir o mecanismo de asserção se importar o código.fonte
A resposta do MSalter é uma boa ideia.
Se o cálculo inteiro for necessário (para precisão), mas o ponto flutuante estiver disponível, você poderá fazer algo como:
fonte
(c * log(a) < max_log)
, ondeconst double max_log = log(UINT_MAX)
O conjunto de instruções x86 inclui uma instrução de multiplicação não assinada que armazena o resultado em dois registros. Para usar essa instrução de C, pode-se escrever o seguinte código em um programa de 64 bits (GCC):
Para um programa de 32 bits, é necessário tornar o resultado em 64 bits e os parâmetros em 32 bits.
Uma alternativa é usar o intrínseco dependente do compilador para verificar o registro do sinalizador. A documentação do GCC para transbordamento intrínseco pode ser encontrada em 6.56 Funções internas para executar aritmética com verificação de estouro .
fonte
__uint128
não assinado para evitar estouros assinados e mudar à direita um valor negativo.fonte
Uma maneira limpa de fazer isso seria substituir todos os operadores (+ e * em particular) e verificar se há um estouro antes de executar as operações.
fonte
Depende do que você usa. Realizando adição ou multiplicação longa não assinada (DWORD), a melhor solução é usar ULARGE_INTEGER.
ULARGE_INTEGER é uma estrutura de dois DWORDs. O valor total pode ser acessado como "QuadPart", enquanto o DWORD alto é acessado como "HighPart" e o DWORD baixo é acessado como "LowPart".
Por exemplo:
fonte
ULARGE_INTEGER
.Para executar uma multiplicação sem sinal sem transbordar de maneira portátil, o seguinte pode ser usado:
fonte
A maneira simples de testar o estouro é fazer a validação, verificando se o valor atual é menor que o valor anterior. Por exemplo, suponha que você tenha um loop para imprimir os poderes de 2:
A adição de verificação de estouro da maneira que descrevi resulta em:
Ele funciona para valores não assinados, bem como valores assinados positivos e negativos.
Obviamente, se você quisesse fazer algo semelhante para diminuir valores em vez de aumentar valores, você iria virar o
<=
sinal para fazê-lo>=
, assumindo que o comportamento do underflow é o mesmo que o comportamento do overflow. Com toda a honestidade, isso é tão portátil quanto você obtém sem acesso ao sinalizador de estouro de uma CPU (e isso exigiria código de montagem em linha, tornando seu código não portátil em implementações de qualquer maneira).fonte