c - resta - suma de binarios 3 filas
La mejor forma de hacer aritmética binaria en C? (5)
Estoy aprendiendo C y escribiendo un programa simple que tomará 2 valores de cadena asumidos para cada uno de ser números binarios y realizar una operación aritmética de acuerdo con la selección del usuario:
- Agregue los dos valores,
- Reste la entrada 2 de la entrada 1, o
- Multiplica los dos valores.
Mi implementación asume que cada carácter en la cadena es un bit binario, por ejemplo, char bin5 = "0101";
, pero parece un enfoque demasiado ingenuo para analizar a través de la cadena un personaje a la vez. Idealmente, me gustaría trabajar directamente con los valores binarios.
¿Cuál es la forma más eficiente de hacer esto en C? ¿Hay una mejor manera de tratar la entrada como valores binarios en lugar de scanf()
y obtener cada bit de la cadena?
Hice algunas investigaciones, pero no encontré ningún enfoque que fuera obviamente mejor desde la perspectiva de un principiante. ¡Cualquier sugerencia sera apreciada!
¿No sería más fácil analizar las cadenas en enteros y luego realizar sus cálculos matemáticos en los enteros?
Asumo que esta es una tarea escolar, pero te estoy invitando a votar porque pareces estar haciendo un buen esfuerzo.
Para obtener el mejor rendimiento, debe distinguir entre las funciones confiables y las que no son de confianza.
Por ejemplo, una función como getBinNum()
que acepta la entrada del usuario, debe ser revisada en busca de caracteres válidos y comprimida para eliminar ceros a la izquierda. Primero, mostraremos una función de compresión in situ de propósito general:
// General purpose compression removes leading zeroes.
void compBinNum (char *num) {
char *src, *dst;
// Find first non-''0'' and move chars if there are leading ''0'' chars.
for (src = dst = num; *src == ''0''; src++);
if (src != dst) {
while (*src != ''/0'')
*dst++ = *src++;
*dst = ''/0'';
}
// Make zero if we removed the last zero.
if (*num == ''/0'')
strcpy (num, "0");
}
A continuación, proporcione una función de comprobación que devuelve el valor pasado o NULL si no es válido:
// Check untested number, return NULL if bad.
char *checkBinNum (char *num) {
char *ptr;
// Check for valid number.
for (ptr = num; *ptr == ''0''; ptr++)
if ((*ptr != ''1'') && (*ptr != ''0''))
return NULL;
return num;
}
Entonces la función de entrada en sí:
#define MAXBIN 256
// Get number from (untrusted) user, return NULL if bad.
char *getBinNum (char *prompt) {
char *num, *ptr;
// Allocate space for the number.
if ((num = malloc (MAXBIN)) == NULL)
return NULL;
// Get the number from the user.
printf ("%s: ", prompt);
if (fgets (num, MAXBIN, stdin) == NULL) {
free (num);
return NULL;
}
// Remove newline if there.
if (num[strlen (num) - 1] == ''/n'')
num[strlen (num) - 1] = ''/0'';
// Check for valid number then compress.
if (checkBinNum (num) == NULL) {
free (num);
return NULL;
}
compBinNum (num);
return num;
}
Se deben escribir otras funciones para agregar o multiplicar para asumir que la entrada ya es válida, ya que habrá sido creada por una de las funciones de esta biblioteca. No proporcionaré el código porque no es relevante para la pregunta:
char *addBinNum (char *num1, char *num2) {...}
char *mulBinNum (char *num1, char *num2) {...}
Si el usuario elige obtener sus datos de un sitio que no sea getBinNum()
, puede permitir que invoquen checkBinNum()
para validarlo.
Si estuvieras realmente paranoico, podrías verificar cada número ingresado en tus rutinas y actuar en consecuencia (devolver NULO), pero eso requeriría controles relativamente caros que no son necesarios.
Primero, te recomiendo que uses cosas como strtol como lo recomienda tgamblin, es mejor usar las cosas que te da la lib en lugar de crear la rueda una y otra vez.
Pero dado que estás aprendiendo que CI hizo una versión pequeña sin strtol, no es ni rápida ni segura, pero jugué un poco con la manipulación de bits como ejemplo.
int main()
{
unsigned int data = 0;
int i = 0;
char str[] = "1001";
char* pos;
pos = &str[strlen(str)-1];
while(*pos == ''0'' || *pos == ''1'')
{
(*pos) -= ''0'';
data += (*pos) << i;
i++;
pos--;
}
printf("data %d/n", data);
return 0;
}
Suponer que una cadena es un número binario simplemente porque consiste solo en dígitos del conjunto {0,1} es peligroso. Por ejemplo, cuando su entrada es "11", el usuario puede haber significado once en decimal, no tres en binario. Es este tipo de descuido lo que da lugar a errores horribles. Su entrada es ambiguamente incompleta y realmente debe solicitar que el usuario especifique la base también.
Consejo:
No hay mucho mejor que marchar a través de la cadena un carácter a la vez y asegurarse de que el usuario ingrese solo unos y ceros. Tenga en cuenta que aunque podría escribir una rutina de ensamblaje realmente rápida si supone que todo es 1
o 0
, realmente no desea hacer eso. El usuario puede ingresar cualquier cosa, y le gustaría poder decirles si se equivocaron o no.
Es cierto que esto parece increíblemente lento en comparación con los ciclos de pareja que probablemente lleva agregar los números reales, pero ¿realmente importa si obtienes tu respuesta en un nanosegundo o un milisegundo? Los humanos solo pueden detectar 30 milisegundos de latencia de todos modos.
Finalmente, ya lleva mucho más tiempo obtener entrada del usuario y escribir salida en la pantalla que analizar la cadena o sumar los números, por lo que su algoritmo difícilmente es el cuello de botella aquí. Guarde sus optimizaciones de lujo para cosas que en realidad son intensivas desde el punto de vista computacional :-).
Lo que debes enfocarte aquí es hacer que la tarea requiera menos mano de obra. Y, resulta que alguien ya hizo eso por ti.
Solución:
Eche un vistazo a la página de strtol()
:
long strtol(const char *nptr, char **endptr, int base);
Esto le permitirá convertir una cadena (nptr) en cualquier base a una larga. También verifica los errores. Uso de muestra para convertir una cadena binaria:
#include <stdlib.h>
char buf[MAX_BUF];
get_some_input(buf);
char *err;
long number = strtol(buf, &err, 2);
if (*err) {
// bad input: try again?
} else {
// number is now a long converted from a valid binary string.
}
La base de suministro 2 le dice a strtol
que convierta literales binarios.