A função \boldsymbol{\operatorname { m a x }} \mathbf{3}() apresentada em quatro implementações diferentes em um dos slides na Aula \mathbf{0 8} recebe três números e retornar o maior dentre eles. A função \boldsymbol{\operatorname { m a x }} \mathbf{3 X} () apresentada a seguir também deveria retornar o maior dentre três números, mas não o faz para todos os valores de entrada.
(a) Apresente uma lista com valores de entrada e os respectivos valores de saída esperados, a qual poderia ser utilizada como teste para verificar se a implementação da função max3X () está correta ou não.
(b) Qual(is) “caso(s)” (valores de entrada) faz(em) com que a função max3X() não retorne o maior dos três números utilizados como valores de entrada?
float max3X( float v1, float v2 , float v3 ) {
    if ( ( v1 > v2 ) && ( v1 > v3 ) )
        return v1;
    if ( ( v2 > v1 ) && ( v2 > v3 ) )
        return v2;
    return v3;
}'

Passo 1

O enunciado dá a função abaixo (em C) que deveria retornar o maior entre três números, mas pode falhar:
\texttt{float max3X( float v1, float v2 , float v3 ) \{}
\texttt{\\ \\ \\ if ( ( v1 > v2 ) \&\& ( v1 > v3 ) )}
\texttt{\\ \\ \\ \\ \\ return v1;}
\texttt{\\ \\ \\ if ( ( v2 > v1 ) \&\& ( v2 > v3 ) )}
\texttt{\\ \\ \\ \\ \\ return v2;}
\texttt{\\ \\ \\ return v3;}
\texttt{\}}
O que o enunciado pede:
(a) Apresentar uma lista de valores de entrada (tripletas) com os respectivos valores de saída esperados — ou seja, um conjunto de testes que permita verificar se a implementação está correta.
(b) Indicar quais caso(s) (quais valores de entrada) fazem com que a função \texttt{max3X()} não retorne o maior dos três números.

Passo 2

Vou explicar claramente como a função funciona e onde pode haver problema — passo a passo, sem pular etapas.
A função compara os números com duas condições sequenciais:
  • Primeiro faz a verificação (v1 > v2) e (v1 > v3). Se ambas forem verdadeiras, retorna v1. Isso cobre o caso em que v1 é estritamente maior que os outros dois.
  • Se a primeira condição falhar, faz a verificação (v2 > v1) e (v2 > v3). Se ambas verdadeiras, retorna v2. Isso cobre o caso em que v2 é estritamente maior que os outros dois.
  • Se nenhuma das duas condições for satisfeita, a função retorna v3.
Observe a palavra chave: “estritamente maior” — o código usa o operador (>), não (\ge). Isso significa que sempre que houver igualdade entre dois valores, as comparações que exigem estrita superioridade podem falhar mesmo que esses valores sejam de fato os máximos (por estarem empatados). É justamente aí que mora o bug: empates entre os dois primeiros argumentos podem fazer a função pular as duas condições e devolver v3, que pode ser menor.

Passo 3

Agora monto uma lista de testes (tripletas) com o resultado esperado em cada caso. Esses testes foram escolhidos para cobrir: todas as ordens possíveis sem empates, empates entre diferentes pares, todos iguais e casos com negativos/decimais — assim você detecta o bug na implementação.
Testes (entrada \to saída esperada):
(1,\;2,\;3) \to 3
(3,\;2,\;1) \to 3
(2,\;3,\;1) \to 3
(5,\;5,\;3) \to 5
(5,\;3,\;5) \to 5
(3,\;5,\;5) \to 5
(5,\;5,\;5) \to 5
(-1,\;-2,\;-3) \to -1
(0,\;0,\;1) \to 1
(1{,}5,\;1{,}5,\;1{,}0) \to 1{,}5
Por quê esses testes?
  • Os três primeiros cobrem permutações sem empates (todas as ordens distintas). A implementação correta deve retornar sempre o maior.
  • (5,5,3) é o teste que expõe o bug potencial: os dois primeiros são iguais e maiores que o terceiro — aqui esperamos 5.
  • (5,3,5) e (3,5,5) verificam empates envolvendo o terceiro elemento emparelhado com outro — são importantes porque a função pode retornar v3 e aí esses casos ajudam a confirmar comportamento.
  • (5,5,5) verifica caso todos iguais.
  • Casos com negativos e decimais verificam que a função lida com sinais e pontos flutuantes corretamente.
Execute esses testes na implementação; se algum deles produzir saída diferente do esperado, a função está errada.

Passo 4

Mostro exatamente por que e quando a função falha — com uma demonstração passo a passo numérica e lógica.
Considere o caso (v1,v2,v3)=(5,5,3). Esperamos 5. Agora avalie a função linha a linha:
  • Primeiro (v1 > v2) é (5 > 5), que é falso (porque não é estritamente maior, é igual). Logo a conjunção (v1 > v2)\\ \&\&\\ (v1 > v3) é falsa, então não retorna v1.
  • Segundo (v2 > v1) é (5 > 5), também falso; portanto a segunda conjunção (v2 > v1)\\ \&\&\\ (v2 > v3) é falsa, então não retorna v2.
  • Como nenhum dos dois ifs retornou, a função executa o \texttt{return\\ }v3; e devolve v3=3.
Resultado: a função devolve 3 quando o correto seria 5. Ou seja, ela falha nesse caso.
Em linguagem geral: qualquer situação em que os dois primeiros argumentos empatam no maior valor e esse valor é maior que o terceiro — isto é, quando
v1 = v2 > v3
faz com que ambas as condições (v1 > v2) e (v2 > v1) sejam falsas, então a função retorna v3 (errado).
Verifique outros empates:
  • Se v1 = v3 > v2: a função retorna v3. Mas como v3 = v1 e esse é o máximo, o valor retornado é correto.
  • Se v2 = v3 > v1: a função retorna v3 (que é igual ao máximo) — correto.
  • Se v1 = v2 = v3: a função retorna v3 — correto (todos iguais).
Portanto o único padrão de entrada que causa erro é quando os dois primeiros argumentos são iguais e esse valor é estritamente maior que o terceiro.

Passo 5

Resumo didático e dica para consertar (para você entender como resolver na prova):
  • A raiz do erro é o uso de comparações estritas (>) em vez de permitir igualdade para cobrir empates. Uma correção simples (que resolve o caso problemático) é usar (\ge) em pelo menos uma das verificações iniciais, por exemplo testar se v1 é maior ou igual aos outros dois antes de retornar v1. Outra alternativa bem comum e robusta é inicializar um valor \text{max} \leftarrow v1 e então fazer:
    • se v2 > \text{max} então \text{max} \leftarrow v2,
    • se v3 > \text{max} então \text{max} \leftarrow v3,
    • retornar \text{max}.
      Esse último método evita problemas de empates e é o padrão que eu usaria na prova porque é simples e fácil de justificar passo a passo.

Resposta

(a) Testes e saídas esperadas:
(1,\;2,\;3)\to 3
(3,\;2,\;1)\to 3
(2,\;3,\;1)\to 3
(5,\;5,\;3)\to 5
(5,\;3,\;5)\to 5
(3,\;5,\;5)\to 5
(5,\;5,\;5)\to 5
(-1,\;-2,\;-3)\to -1
(0,\;0,\;1)\to 1
(1{,}5,\;1{,}5,\;1{,}0)\to 1{,}5
(b) A função falha quando os dois primeiros argumentos empatam e são maiores que o terceiro, isto é, para entradas que satisfazem \;v1 = v2 > v3\; (por exemplo (5,5,3), que faz a função retornar 3 em vez de 5).

Exercícios relacionados