Expliquei aos meus alunos que o teste igual a não é confiável para variáveis de flutuação, mas é bom para números inteiros. O livro que estou usando disse que é mais fácil ler> e <que> = e <=. Eu concordo até certo ponto, mas em um loop For? Não é mais claro que o loop especifique os valores inicial e final?
Estou perdendo algo que o autor do livro está correto?
Outro exemplo está nos testes de alcance, como:
se pontuação> 89 nota = 'A'
senão se pontuação> 79 nota = 'B' ...
Por que não dizer apenas: se pontuação> = 90?
Respostas:
Em linguagens de programação com chaves baseadas em zero , é habitual escrever
for
loops como este:Isso percorre todos os elementos da matriz e é de longe o caso mais comum. Evita o uso de
<=
ou>=
.O único momento em que isso precisaria mudar é quando você precisa pular o primeiro ou o último elemento, atravessá-lo na direção oposta ou atravessá-lo de um ponto inicial diferente ou de um ponto final diferente.
Para coleções, em idiomas que suportam iteradores, é mais comum ver o seguinte:
O que evita totalmente as comparações.
Se você está procurando uma regra rígida e rápida sobre quando usar o
<=
vs<
, não há uma; use o que melhor expressa sua intenção. Se seu código precisar expressar o conceito "Menor ou igual a 55 milhas por hora", será necessário dizer<=
não<
.Para responder sua pergunta sobre os intervalos de notas,
>= 90
faz mais sentido, porque 90 é o valor limite real, não 89.fonte
for
loops como esse. A forma defor
loop que forneci aqui será instantaneamente reconhecível por qualquer desenvolvedor com um pouco de experiência. Se você deseja uma resposta mais específica com base em um cenário mais específico, inclua-a na sua pergunta.Não importa.
Mas, para o bem do argumento, vamos analisar as duas opções:
a > b
vsa >= b
.Espere! Aqueles não são equivalentes!
OK, então
a >= b -1
vsa > b
oua > b
vsa >= b +1
.Hum,
a >b
ea >= b
ambos parecem melhores quea >= b - 1
ea >= b +1
. O que são todos esses1
s afinal? Então, eu argumentaria que qualquer benefício de ter em>
vez de>=
ou vice-versa é eliminado pela necessidade de adicionar ou subtrair1
s aleatórios .Mas e se for um número? É melhor dizer
a > 7
oua >= 6
? Espere um segundo. Estamos discutindo seriamente se é melhor usar>
vs>=
e ignorar as variáveis codificadas? Então, torna-se realmente uma questão de saber sea > DAYS_OF_WEEK
é melhor do quea >= DAYS_OF_WEEK_MINUS_ONE
... ou éa > NUMBER_OF_LEGS_IN_INSECT_PLUS_ONE
vsa >= NUMBER_OF_LEGS_IN_INSECT
? E voltamos a adicionar / subtrair1
s, apenas desta vez em nomes de variáveis. Ou talvez debatendo se é melhor usar limiar, limite, máximo.E parece que não há regra geral: depende do que está sendo comparado
Mas, realmente, há coisas muito mais importantes a serem aprimoradas no código de alguém e diretrizes muito mais objetivas e razoáveis (por exemplo, limite de caracteres X por linha) que ainda têm exceções.
fonte
>
vs>=
ou se a discussão de>
vs>=
é significativa? embora seja provavelmente melhor para evitar discutir isto: pComputacionalmente, não há diferença de custo ao usar
<
ou>
comparar com<=
ou>=
. É calculado igualmente rápido.No entanto, a maioria dos loops será contada a partir de 0 (porque muitos idiomas usam a indexação 0 para suas matrizes). Portanto, o loop for canônico nessas línguas é
fazer isso com a
<=
exigiria que você adicionasse -1 em algum lugar para evitar o erro de desvioou
Obviamente, se o idioma usar indexação baseada em 1, você usaria <= como condição limitadora.
A chave é que os valores expressos na condição são os da descrição do problema. É mais limpo ler
por um intervalo semiaberto que
e precisa fazer as contas para saber que não há valor possível entre 19 e 20
fonte
for(markup = 5; markup <= MAX_MARKUP; ++markup)
. Qualquer outra coisa seria complicar demais.Eu diria que o ponto não é se você deve usar> ou> =. O objetivo é usar o que lhe permite escrever código expressivo.
Se você achar que precisa adicionar / subtrair um, considere usar o outro operador. Acho que coisas boas acontecem quando você começa com um bom modelo de seu domínio. Então a lógica se escreve.
Isso é muito mais expressivo do que
Noutros casos, é preferível o contrário:
Muito melhor que
Por favor, desculpe a "obsessão primitiva". Obviamente, você gostaria de usar os tipos Velocity e Money aqui, respectivamente, mas eu os omiti por brevidade. O ponto é: use a versão mais concisa e que permita que você se concentre no problema de negócios que deseja resolver.
fonte
Como você apontou na sua pergunta, o teste de igualdade em variáveis flutuantes não é confiável.
O mesmo vale para
<=
e>=
.No entanto, não existe esse problema de confiabilidade para tipos inteiros. Na minha opinião, o autor estava expressando sua opinião sobre qual é mais legível.
Se você concorda ou não com ele, é claro que é sua opinião.
fonte
<
ou com<=
base no que é mais natural para o problema específico que estou resolvendo. Como outros já apontaram, em um loop FOR<
faz mais sentido em linguagens do tipo C. Existem outros casos de uso que favorecem<=
. Use todas as ferramentas à sua disposição, quando e onde apropriado.for (unsigned int i = n; i >= 0; i--)
oufor (unsigned int i = x; i <= y; i++)
sey
acontece de serUINT_MAX
. Opa, esses loop para sempre.Cada uma das relações
<
,<=
,>=
,>
e também==
e!=
têm seus casos de uso para comparar dois valores de ponto flutuante. Cada um tem um significado específico e o apropriado deve ser escolhido.Vou dar exemplos de casos em que você deseja exatamente esse operador para cada um deles. (Esteja ciente dos NaNs, no entanto.)
f
que assume um valor de ponto flutuante como entrada. Para acelerar seus cálculos, você decide adicionar um cache dos valores computados mais recentemente, ou seja, um mapeamento de tabela de pesquisax
paraf(x)
. Você realmente deseja usar==
para comparar os argumentos.x
? Você provavelmente quer usarx != 0.0
.x
está no intervalo da unidade?(x >= 0.0) && (x < 1.0)
é a condição correta.d
de uma matriz e deseja saber se é definitivo positivo? Não há razão para usar qualquer outra coisa, excetod > 0.0
.alpha <= 1.0
.A matemática de ponto flutuante (em geral) não é exata. Mas isso não significa que você deve temê-lo, tratá-lo como mágica e, certamente, nem sempre tratar duas quantidades de ponto flutuante iguais se estiverem dentro
1.0E-10
. Fazer isso realmente quebrará sua matemática e fará com que todas as coisas estranhas aconteçam.x != 0.0
ey
seja um valor finito de ponto flutuante,y / x
não precisa ser finito. Mas pode ser relevante saber sey / x
não é finito por causa do estouro ou porque a operação não foi matematicamente bem definida para começar.x
no intervalo de unidades [0, 1), ficaria muito chateado se disparasse uma falha de asserção quando chamado comx == 0.0
oux == 1.0 - 1.0E-14
.1.0E-30
, nada é ganho. Tudo o que você fez foi aumentar a probabilidade de dar a resposta errada.alpha
pode ser afetado por erros de arredondamento e, portanto,alpha <= 1.0
pode ser verdadeiro, mesmo que o verdadeiro valor matemático para a expressão tenhaalpha
sido calculado possa ter sido realmente maior que 1. Mas não há nada que você possa fazer sobre isso neste momento.Como sempre na engenharia de software, lide com os erros no nível apropriado e lide com eles apenas uma vez. Se você adicionar erros de arredondamento na ordem de
1.0E-10
(este parece ser o valor mágico que a maioria das pessoas usa, não sei por quê) toda vez que comparar quantidades de ponto flutuante, em breve você encontrará erros na ordem de1.0E+10
...fonte
O tipo de condicional usado em um loop pode limitar os tipos de otimizações que um compilador pode executar, para melhor ou para pior. Por exemplo, dado:
um compilador pode assumir que a condição acima deve fazer com que o loop saia após o enésimo enésimo passo, a menos que n possa 65535 e o loop possa sair de alguma outra maneira que não seja i excedendo n. Se essas condições se aplicarem, o compilador deverá gerar um código que faça com que o loop seja executado até que algo diferente da condição acima faça com que ele saia.
Se o loop tivesse sido escrito como:
então, um compilador poderia assumir com segurança que o loop nunca precisaria ser executado mais de n vezes e, portanto, seria capaz de gerar código mais eficiente.
Observe que qualquer estouro com tipos assinados pode ter consequências desagradáveis. Dado:
Um compilador pode reescrever isso como:
Esse loop se comportaria de forma idêntica ao original se não ocorrer um estouro nos cálculos, mas poderá ser executado para sempre, mesmo em plataformas de hardware onde o excesso de números normalmente teria semântica de empacotamento consistente.
fonte