You can't use scanf
with %lld
to input a binary number string (e.g. 10101010101010101010101010101010101
). It will interpret the number incorrectly.
You have to accept it as a char
string (e.g).
char str[100];
scanf("%s",str);
But, scanf
is problematic because it can overflow str
. And, IMO, the "usual" remedy of:
scanf("%99s",str);
Is a hack [because if we ever change to (e.g.) char str[80];
then we have to manually change the scanf
as well].
Better to use fgets
:
char str[100];
fgets(str,sizeof(str),stdin);
str[strcspn(str,"\n")] = 0;
Then, loop on the string. No need for power*
functions. You want:
long long int decimal(const char *e){
Loop on e
and do: k <<= 1;
Here is the refactored code:
#include <stdio.h>
#include <string.h>
long long int
decimal(const char *e)
{
long long int result = 0;
for (int chr = *e++; chr != 0; chr = *e++) {
result <<= 1;
switch (chr) {
case '0':
case '1':
result += (chr - '0');
break;
default:
fprintf(stderr,"decimal: not a binary digit -- chr='%c'\n",chr);
break;
}
}
return result;
}
int
main(void)
{
char str[100];
while (1) {
printf("Write down a binary number.\n");
if (fgets(str,sizeof(str),stdin) == NULL)
break;
// strip newline
str[strcspn(str,"\n")] = 0;
long long int a = decimal(str);
printf("The binary number '%s' converted into decimal is: %lld (hex: %llX)\n",
str, a, a);
}
return 0;
}
Here is some sample output:
Write down a binary number.
110010111001
The binary number '110010111001' converted into decimal is: 3257 (hex: CB9)
Write down a binary number.
UPDATE:
Apart from the possibility of "overfilling" the register with bits (accepting a string that is more than 32 or 64 'bits'), someone here on SO recently pointed out that left shifting (possibly into the sign bit) a "signed" value can invoke UB... –
Fe2O3
I'm not sure it's UB (depending on the context). But, to keep the peace, here's a version that sets errno
:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#ifndef UNSIGNED
#define UNSIGNED 0
#endif
#if UNSIGNED
typedef unsigned long long num_t;
#define FMT "%llu"
#else
typedef long long num_t;
#define FMT "%lld"
#endif
num_t
decimal(const char *e)
{
int count = 0;
long long int result = 0;
errno = 0;
// NOTE: use of error codes is arbitrary
for (int chr = *e++; chr != 0; chr = *e++) {
result <<= 1;
if (++count > (63 + UNSIGNED)) {
fprintf(stderr,"decimal: too many digits\n");
errno = E2BIG;
break;
}
switch (chr) {
case '0':
case '1':
result += (chr - '0');
break;
default:
fprintf(stderr,"decimal: not a binary digit -- chr='%c'\n",chr);
errno = EINVAL;
break;
}
if (errno)
break;
}
return result;
}
int
main(void)
{
char str[100];
while (1) {
printf("Write down a binary number.\n");
if (fgets(str,sizeof(str),stdin) == NULL)
break;
// strip newline
str[strcspn(str,"\n")] = 0;
num_t a = decimal(str);
printf("The binary number '%s' converted into decimal is: " FMT " (hex: %llX)\n",
str, a, a);
}
return 0;
}
Caveat: The following is off topic for this question, but, in response to the comments below ...
Neither of those, although... No, the DV for braces (not an anonymous drive-by shooting!) was for stackoverflow.com/a/74010599/17592432, the most trivial game on the planet... :-) /sigh/ –
Fe2O3
I agree that leaving off the curly braces is not ambiguous. But, I would add them for human clarity. Note that GNU indent
complains about premature EOF when trying to indent the code.
But, I eschew if/else
ladder logic. In your block, using an if
and then switch/case
would be clearer, IMO.
And, I've never liked else if
because to me, to properly indent it, we'd have:
if (user == robot)
printf("It's a tie!");
else
if (user == 1)
if (robot == 2)
printf("Robot wins by choosing Paper!");
else
printf("User wins by choosing Rock!");
else
if (user == 2)
if (robot == 1)
printf("User wins by choosing Paper!");
else
printf("Robot wins by choosing Scissors! ");
else
if (user == 3)
if (robot == 2)
printf("User wins by choosing Scissors!");
else
printf("Robot wins by choosing Rock!");
So, [to further muddy the waters] I prefer using do { ... } while (0)
:
do {
if (user == robot) {
printf("It's a tie!");
break;
}
if (user == 1) {
if (robot == 2)
printf("Robot wins by choosing Paper!");
else
printf("User wins by choosing Rock!");
break;
}
if (user == 2) {
if (robot == 1)
printf("User wins by choosing Paper!");
else
printf("Robot wins by choosing Scissors! ");
break;
}
if (user == 3) {
if (robot == 2)
printf("User wins by choosing Scissors!");
else
printf("Robot wins by choosing Rock!");
break;
}
} while (0);
UPDATE #2:
May at least want to mention POSIX and _t types (I'm guilty as charged on the use as well) –
David C. Rankin
Sigh, I was hoping to have a quiet day ;-)
First, I like [love] POSIX (vs. ISO/C). But, claiming all *_t
and all _*
as POSIX only is hubris.
The _*
[for private functions] is quite common in other languages (e.g. python
).
As to *_t
types, I've been doing that for decades and never once hit a conflict. And, if I did, it's my responsibility as the programmer to fix that [by changing my code or the #include
statements]. That is, assess the risks beforehand.
Worse, I also do (e.g.) typedef struct foo foo_t, *foo_p;
. Note the *_p
for a pointer type that seems to burn people here on SO.
But, it's arguably no worse that MS/Win's pType
for pointers. And, it's my convention. And, it's passed code reviews plenty of times.
So, as Nixon once said (re. "Checkers"): Regardless of what they say about it, we're gonna keep it.