Test Foro de elhacker.net SMF 2.1

Programación => Programación C/C++ => Mensaje iniciado por: juntacadaveres en 5 Junio 2020, 14:14 PM

Título: problema if anidados (notas)
Publicado por: juntacadaveres en 5 Junio 2020, 14:14 PM
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
Título: Re: problema if anidados (notas)
Publicado por: K-YreX en 5 Junio 2020, 14:55 PM
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.
Título: Re: problema if anidados (notas)
Publicado por: Loretz en 5 Junio 2020, 15:13 PM
 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

Título: Re: problema if anidados (notas)
Publicado por: ThunderCls en 5 Junio 2020, 18:07 PM
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
Título: Re: problema if anidados (notas)
Publicado por: Loretz en 5 Junio 2020, 20:38 PM
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";
}
}


Título: Re: problema if anidados (notas)
Publicado por: CalgaryCorpus en 6 Junio 2020, 17:45 PM
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.