problema if anidados (notas)

Iniciado por juntacadaveres, 5 Junio 2020, 14:14 PM

0 Miembros y 1 Visitante están viendo este tema.

juntacadaveres

hola estoy aprendido a programar en C y estaba probando los if / else e hice este codigo que recoge la nota de un alumno y le dice si ha aprobado o no
el codigo es el siguiente:

#include <stdio.h>
int main (void) {
   float nota;
   printf ("Cuanta nota has sacado?: ");
   scanf ("%f", &nota);
   fflush (stdin);
   printf ("Ha sacado la siguiente nota: %f", nota);
   /* empiezan los if anidados */
   if ((nota>=9) && (nota<=9.9)) {
       printf ("\nEn hora buena, tienes un SOBRESALIENTE");
   } else {
       if (nota==10) {
           printf ("\nEn hora buena! tienes una matricula de honor");
       } else {
           if ((nota >=0) && (nota <5))  {
               printf ("\nLo sentimos, pero no lograste aprobar :( ");
           } else {
               if ((nota>=5)&& (nota<=6.9)) {
                   printf ("\nHas aprobado!!");
               } else {
                   if ((nota>=7)&& (nota<=8.9)) {
                       printf ("\nTienes un NOTABLE");
                   } else {
                       if (nota>10) {
                           printf ("\nNo puedes tener mas de un 10");
                       } else {
                           if (nota < 0) {
                               printf ("\nNo puedes poner numeros NEGATIVOS");
                           }
                       }
                   }
               }
           }
       }

   }

   getchar();
   return 0;

}


en sí el código a simple vista tiene que funcionar
y funciona!
el unico problema es cuando introduzco como nota 6.9. me debería salir por pantalla el mensaje de que he aprobado, pues bien, no lo saca, y no sé por qué si al intentar otros valores si me muestra el resultado esperado

K-YreX

El problema está en esos ".9".
En ningún momento aseguras que la nota tenga un solo decimal, entonces qué pasa si tienes un 9.95, 9.995, 9.999995, 6.99999999??? Todas esas notas no están contempladas en ningún caso.

Es por esto que no se usa (<= 9.9) sino que se usa (< 10) que sí contempla cualquier cantidad de decimales. Así que tendrías que sustituirlo en todos los casos y cuando sustituyas (<= 6.9) por (< 7) verás que funciona correctamente. El problema será que aunque tú escribas 6.9, un float (32 bits) maneja una cantidad muy grande de decimales, entonces es posible que se guarde 6.90[...]01 y entonces no se cumple la condición.

Aparte de eso, te doy algún consejo más aunque no te frustres por no haber hecho el mejor programa. Es normal cuando uno está empezando. Poco a poco irás mejorando en la lógica.
  • Para limpiar el buffer no se debe utilizar nunca fflush(stdin). Esta función está definida por el lenguaje para limpiar el buffer de salida (fflush(stdout)) y hasta la propia documentación del lenguaje recoge que el uso con stdin es indeterminado. Puede ser que 1 millón de veces pero no hay fundamentación teórica que diga que va a funcionar 1 millón y 1 vez.
    Una alternativa para limpiar el buffer es:
    while(getchar() != '\n');
    Por si te interesa el funcionamiento: el buffer contiene lo que introduces por teclado para luego usarlo (en una asignación a una variable por ejemplo). Puede ser que queden caracteres que no cabían en la variable y al final como último elemento del buffer el ENTER ('\n'). Con la instrucción anterior vas cogiendo cada caracter que queda en el buffer hasta encontrar el ENTER (que lo coge también). Una vez cogido el ENTER, ese bucle termina y el buffer está vacío.

  • Si ordenas los rangos de los ifs verás que queda un código más simple y con menos condiciones. Además los else-if se suelen encadenar como te muestro acontinuación pues es más fácil de leer y ahorran llaves:

    if(nota < 0){
      printf("La nota es menor que 0. Intervalo: (-inf,0)");
    } else if(nota < 5){ // Si llegamos a este else, la condicion anterior es falsa por lo tanto ya sabemos que el numero es >= 0. No hay que comprobarlo otra vez
        printf("La nota esta entre 0 y 5 sin llegar al 5. Intervalo: [0,5)");
      } else if(nota < 7){ // Si llegamos aqui ya sabemos que nota >= 0 por el primer if y que nota >= 5 por el segundo. No hay que comprobarlo.
          printf("La nota esta entre 5 y 7 sin llegar al 7. Intervalo: [5,7)");
        } else if(nota < 9){ // Igual que antes. Ya sabemos que la nota es >= 7.
             printf("La nota esta entre 7 y 9 sin llegar al 9. Intervalo: [7,9)");
           } else if(nota < 10){ // Ya sabemos que la nota es >= 9
                printf("La nota esta entre 9 y 10 sin llegar al 10. Intervalo: [9,10)");
              } else if(nota == 10){
                   printf("La nota es 10");
                 } else { // Igual que antes si ninguno de los ifs anteriores se han cumplido, ya sabemos que la nota es > 10
                      printf("La nota es mayor que 10. Intervalo: (10, +infinito)");
                    }


    Si has utilizado los intervalos de matemáticas puedes ver la diferencia de usar intervalo abierto o cerrado. En la mitad abierta (con paréntesis) no se incluye ese valor pero en la cerrada (con corchetes), sí. Ese matiz es la diferencia entre usar el < / > o el <= / >=.

    Además suele quedar un poco feo eso de que el código se vaya tanto hacia el centro y cuando los bloques (condicionales, bucles,...) están formados por una única línea en su interior se pueden omitir las llaves. Muchas veces lo verás así:

    if(nota < 0)
      printf("La nota es menor que 0");
    else if(nota < 5)
      printf("La nota esta entre 0 y 5");
    else if(nota < 7)
      printf("La nota esta entre 5 y 7");
    //...


    PD: Por otra parte, muy bien usado el getchar() al final para hacer una pausa en vez de utilizar getch() o system("pause"). ;-)
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

Loretz

 No he probado tu código, pero algo que no debes hacer es comparar floats o doubles con ninguno de los operadores <, ≤, ==, > o ≥
La razón es que lo que cuando escribes un número decimal, 6.9, por ejemplo, estás escribiendo en base 10, mientras que la máquina tiene que traducirlo a base 2 para poder operar, y luego lo volverá a traducir a base 10 para poder mostrártelo, pero... cuando pones algo así:

float f = 6.9;
if (f ≤ 6.9) {
// etcétera
}

en primer lugar 6.9 es de tipo double, si quieres que sea float debes poner 6.9f; porque estarás comparando dos números con precisiones distintas.
Luego, aunque escribas float f = 6.9f;
esa primer traducción de decimal a binario la va a hacer el compilador, y esa representación en binario no es exacta, es como cuando con números decimales escribes 1/3, que en decimal es 0.33333... periódico, de modo que cuando quieras comparar 1/3 == 0.3333333, no te sorprenda que el resultado sea false.

Entonces, la regla general es que tanto doubles como floats no se comparan de esa manera. Y entonces ¿cómo? Yo no conozco una solución única que sirva para todos los casos, pero en tu contexto, para mí bastaría con:
Código (cpp) [Seleccionar]

if ((nota>=5)&& (nota<=6.998f)) {
      printf ("\nHas aprobado!!");
}


Y lo mismo habría que hacer con cada una de las comparaciones.

Pero ésta sería una solución simple para este contexto, en otros casos se requerirá mayor cuidado y complejidad. Puedes ver una discusión sobre este asunto en
https://stackoverflow.com/questions/17333/what-is-the-most-effective-way-for-float-and-double-comparison


ThunderCls

Argumentando un poco mas lo que te han dicho anteriormente, la comparacion de decimales no es tan simple como con numeros enteros y la mayor diferencia radica en la precision. Para mas teoria al respecto puedes ver

https://es.wikipedia.org/wiki/Formato_en_coma_flotante_de_simple_precisi%C3%B3n
https://es.wikipedia.org/wiki/Formato_en_coma_flotante_de_doble_precisi%C3%B3n

En este caso otra posible solucion que se refirio seria usar | a - b | ≤  ϵ; donde ϵ es la constante epsilon. Luego tienes que tener en cuenta que dependiendo de los valores que deseas comparar te servira mas un acercamiento que otro, para este tema no hay un algoritmo o un metodo infalible para todos los casos. Para tu ejemplo en concreto esto tambien te sirve:

#define fleq(a, b) ((((a - b) < FLT_EPSILON) && (fabs(a - b) > FLT_EPSILON)) || (fabs(a - b) < FLT_EPSILON) ? 1 : 0)

Uso:

if ((nota >= 5) && fleq(nota, 6.9))
{
    printf ("\nHas aprobado!!");
}
else
{
    printf ("\nMehhh!!");
}


Saludos
-[ "...I can only show you the door. You're the one that has to walk through it." – Morpheus (The Matrix) ]-
http://reversec0de.wordpress.com
https://github.com/ThunderCls/

Loretz

Un comentario para los que se atreven con el C++20:

Aunque todavía no está terminado de cocer, es el estándar que va a venir antes de fin de año. Y bien, en C++20 estará disponible le "spaceship operator", que formalmente se llama "three-way comparison", y hoy funciona en Visual Studio y la mayoría de los compiladores modernos en modo experimental.

En este caso, para comparar floats o doubles, que podrían llegar a ser NaN (not a number), se necesita una comparación de tipo "partial_ordering".

Bueno, no es que yo sea un experto en naves espaciales, pero vale en este caso:

Código (cpp) [Seleccionar]
#include <iostream>
#include <compare>

int main()
{
float a = 6.9f;
auto my_partial_ordering = a <=> 6.9f;

if (std::is_lt(my_partial_ordering)) {
std::cout << "a < 6.9";
}
else if (std::is_eq(my_partial_ordering)) {
std::cout << "a == 6.9";
}
else if (std::is_gt(my_partial_ordering)) {
std::cout << "a > 6.9";
}
}



CalgaryCorpus

Un truco sucio es multiplicar el valor inicial por 10, tomar la parte entera y hacer toda la logica con numeros enteros entre 0 y 100.
Aqui mi perfil en LinkedIn, invitame un cafe aqui