I have been asked to convert a number in base 20 to decimal,
where the number in base 20 is entered by the user in reverse order.
For example if the original number is F1 the input is 1F.
I can only use the standard library <stdio.h>, loops and conditions.
This is what I have done so far that does not seem to work.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
int sum = 0;
int exp = 1;
char reverse_char;
printf("Enter a reversed number in base 20:");
scanf(" %c", &reverse_char);
while (reverse_char != '\n') {
if ((reverse_char >= '0') && (reverse_char < '10') || ((reverse_char >= 'a') && (reverse_char < 'k')) || ((reverse_char >= 'A') && (reverse_char < 'k'))) {
if ((reverse_char >= '0') && (reverse_char < '10')) {
reverse_char = reverse_char - '0';
sum += (reverse_char * exp);
exp = exp * 20;
}
else if ((reverse_char >= 'a') && (reverse_char < 'k')) {
reverse_char = reverse_char - 'a';
sum += (reverse_char * exp);
exp = exp * 20;
}
else
{
reverse_char = reverse_char - 'A';
sum += (reverse_char * exp);
exp = exp * 20;
}
}
else {
printf("%c is not a number in base 20", reverse_char);
break;
}
scanf(" %c", &reverse_char);
}
if (reverse_char == '\n')
printf("%d", sum);
}
At least these problems:
" %c" consumes leading white-space like '\n'
With scanf(" %c", &reverse_char); while (reverse_char != '\n') , reverse_char != '\n' is always true. This certainly contributes to "does not seem to work." as loop does not end.
scanf("%c", &reverse_char); (no space before %c) in two lines of code may work here.
No character 10 #tkausl
Test against '9'.
// if ((reverse_char >= '0') && (reverse_char < '10')) {
if ((reverse_char >= '0') && (reverse_char <= '9')) {
This hints that OP is not enabling all compiler warnings. Best take-away here:
Enable all warnings to save time.
int overflow
exp = exp * 20; can readily overflow int math even if sum in range.
For those EBCDIC aficionados: Character encoding
Below fails as the letter A-J are not all sequentially encoded.
//if ((reverse_char >= 'a') && (reverse_char < 'k')) {
// reverse_char = reverse_char - 'a';
Recommend to use an array look-up.
Any base (limited by the digits string) reversed number converting function
static const char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
uint32_t toU32Reverse(const char *number, int base)
{
uint32_t result = 0;
const char *start = number;
if(number && *number && base < sizeof(digits))
{
while(*(number)) number++;
do
{
const char *pos;
number--;
if((pos = strchr(digits, toupper(*number))) && (pos - digits) < base)
{
result *= base;
result += pos - digits;
}
else
{
result = 0;
break;
}
}while(number > start);
}
return result;
}
Related
I'm writing a function that can read integers expressed in hexadecimal.
This is my code. The main function should not be modified.
#include <stdio.h>
#include <ctype.h>
#define SIZE 100
main()
{
int array[SIZE], n;
int gethex(int*);
int i, sum;
for (n = 0; n < SIZE && gethex(&array[n]) != EOF; n++)
;
sum = 0;
for (i = 0; i < n; i++)
sum += array[i];
printf("The sum is %d\n", sum);
}
int gethex(int *pn)
{
int c, sign;
c = getchar();
while (isspace(c))
;
if (!isxdigit(c) && c != EOF && c != '+' && c != '-') {
ungetc(c, stdin);
return 0;
}
sign = (c == '-') ? -1 : 1;
if (c == '+' || c == '-')
c = getchar();
for (*pn = 0; isxdigit (c); c = getchar()) {
if (c >= '0' && c <= '9') {
*pn = 16 * *pn + (c - '0');
}
else if (c >= 'a' && c <= 'f') {
*pn = 16 * *pn + (c - 'a' + 10);
}
else if (c >= 'A' && c <= 'F') {
*pn = 16 * *pn + (c - 'A' + 10);
}
}
*pn *= sign;
if (c == EOF) {
*pn = c;
return c;
}
}
The result value should come out like this.
-FFec
10000
^Z
The sum is 20
However, my code outputs
The sum is 1717986860
What's wrong?
At least these problems:
Warnings not fully enabled
Warnings are not fully enabled, losing OP and others valuable time. #Jens
End of gethex() lacks a return value.
warning: control reaches end of non-void function [-Wreturn-type]
Infinite loop
Once a '\n is read, loop never ends. #Weather Vane
while (isspace(c))
;
Non hex input also set up an infinite loop with ungetc(c, stdin); as that same c will repeatedly get read on the next function call.
if (!isxdigit(c) && c != EOF && c != '+' && c != '-') {
ungetc(c, stdin);
return 0;
}
int overflow possible
Code like *pn = 16 * *pn + (c - '0'); and *pn *= sign; can lead to int overflow, which is undefined behavior (UB). Robust code would prevent that.
Some untested code to fix these issues.
// Return 0 if non-numeric input detected.
// Return 2 when input out of int range
// Return 1 on success
// Return EOF when input is only white-space, sign or none
int gethex(int *pn) {
int ch;
// Consume leading white-space
do {
ch = getchar();
} while (isspace(ch));
// Get sign, if any
int sign = ch; // remember 1st
if (ch == '-' || ch != '+') {
ch = getchar();
}
if (ch == EOF) {
return EOF;
}
if (!isxdigit(ch)) {
// Consume non-numeric input
return 0;
// Perhaps better to return EOF here to stop calling code.
}
int overflow = 0;
int sum = 0;
while (isxdigit(ch)) {
int digit = 0;
if (isdigit(ch)) {
digit = ch - '0';
} else if (isupper(ch)) {
digit = ch - 'A' + 10;
} else if (islower(ch)) {
digit = ch - 'a' + 10;
}
if (sum <= INT_MIN / 16
&& (sum < INT_MIN / 16 || digit > -(INT_MIN % 16))) {
overflow = 1;
sum = INT_MIN;
} else {
// Accumulate as a negative number as there are more
// negative vales than positive ones.
// This prevents bad code like -INT_MIN later.
sum = sum * 16 - digit;
}
}
if (sign != '-') {
if (sum < -INT_MAX) {
overflow = 1;
sum = INT_MAX;
} else {
sum = -sum;
}
}
*pn = sum;
ungetc(ch, stdin);
return overflow ? 2 : 1;
}
When I run my code, the outcome has failed to show the exact result based on the texts given in the problem set. Although, it showed the grade, the result is incorrect. The text is : "Would you like them here or there? I would not like them here or there. I would not like them anywhere."(Grade 2)
enter image description here
Supposedly, the result for the text is "Grade 2". However, it shows all grades instead.
enter code here
int main(void)
{
string s = get_string("Text: ");
printf("%s\n",s);
int count_letters = 0; //To count letters (uppercase & lowercase)
int count_words = 1; //To count words
int count_sentences = 0; //To count sentences
for (int i = 0; i < strlen(s); i++)
if (isalpha(s[i]))
{
if ((s[i] >= 'a' && s[i] <= 'z' )||( s[i] >= 'A' && s[i] <= 'Z'))
{
count_letters++;
}
if (s[i] == ' ')
{
count_words++;
}
if (s[i] == '.' || s[i] =='!' || s[i] == '?')
{
count_sentences++;
}
//printf("%i count_letter(s)\n", count_letters);
//printf("%i count_words(s)\n", count_words);
//printf("%i sentence(s)\n", count_sentences);
//Coleman-Liau index
float L = (count_letters / (float) count_words) * 100;
float S = (count_sentences / (float) count_words) * 100;
int grade = round (0.0588 * L - 0.296 * S -15.8);
if (grade < 1)
{
printf("Before Grade 1\n");
}
else if (grade >= 16)
{
printf("Grade 16+\n");
}
else
{
printf("Grade %.d\n", grade);
}
}
}
Is there any problem with my code? How can I fix my code in order to receive the exact outcome. I've been doing this problem set for almost 2 days :'/. Thanks in advance
Calculate the number of letters, sentences, and words inside of the loop and calculate Coleman-Liau's index outside of the loop.
Don't calculate something in a loop and try to get specific output from within it as well, it never ends well. So in conclusion, calculate your values in the loop and do everything else outside of it.
int count_letters = 0; //To count letters (uppercase & lowercase)
int count_words = 1; //To count words
int count_sentences = 0; //To count sentences
for (int i = 0; i < strlen(s); i++){
// get the amounts in the loop
if (isalpha(s[i]))
{
if ((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z'))
{
count_letters++;
}
if (s[i] == ' ')
{
count_words++;
}
if (s[i] == '.' || s[i] == '!' || s[i] == '?')
{
count_sentences++;
}
}
}
//Calculate Coleman-Liau outside of it and get the correct grade from your if statements
Since you have three distinct categories to count, I would create a function for each of these.
For example, based on your code, you could create a function to count characters (you don't need isdigit function here, non-digit characters are already filtered out by the algorithm itself):
int get_letters_count(char *text_str)
{
int count_letters = 0;
int text_len = strlen(text_str);
for (int i = 0; i < text_len; i++) {
if ( (text_str[i] >= 'a' && text_str[i] <= 'z')
|| (text_str[i] >= 'A' && text_str[i] <= 'Z')
|| (text_str[i] >= '0' && text_str[i] <= '9')) {
count_letters++;
}
}
return count_letters;
}
This approach of breaking down your program will make it much easier to develop.
Here is a very crude implementation based on the Coleman–Liau index Wikipedia page:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
int get_letters_count(char *text_str)
{
int count_letters = 0;
int text_len = strlen(text_str);
for (int i = 0; i < text_len; i++) {
if ( (text_str[i] >= 'a' && text_str[i] <= 'z')
|| (text_str[i] >= 'A' && text_str[i] <= 'Z')
|| (text_str[i] >= '0' && text_str[i] <= '9')) {
count_letters++;
}
}
return count_letters;
}
int get_words_count(char *text_str)
{
int count_words = 0;
int text_len = strlen(text_str);
for (int i = 0; i < text_len; i++) {
if (text_str[i] == ' ') {
count_words++;
}
}
if (count_words)
count_words++;
return count_words;
}
bool word_is_acronym(char *word)
{
bool ret = true;
for (; *word && *word != ' '; word++) {
if ( *word != '.'
&& *word < 'A' || *word > 'Z') {
ret = false;
}
}
return ret;
}
int get_sentences_count(char *text_str)
{
int count_sentences = 0;
int text_len = strlen(text_str);
char *last_word = &text_str[0];
for (int i = 0; i < text_len; i++) {
if ( text_str[i] == ' '
&& i < (text_len - 1)) {
last_word = &text_str[i + 1];
}
bool end_mark = text_str[i] == '.'
|| text_str[i] == '!'
|| text_str[i] == '?';
if ( end_mark
&& word_is_acronym(last_word) == false) {
count_sentences++;
}
}
return count_sentences;
}
int main(void)
{
char text_str[] = "Existing computer programs that measure readability are based "
"largely upon subroutines which estimate number of syllables, "
"usually by counting vowels. The shortcoming in estimating syllables "
"is that it necessitates keypunching the prose into the computer. "
"There is no need to estimate syllables since word length in letters "
"is a better predictor of readability than word length in syllables. "
"Therefore, a new readability formula was computed that has for its "
"predictors letters per 100 words and sentences per 100 words. "
"Both predictors can be counted by an optical scanning device, and "
"thus the formula makes it economically feasible for an organization "
"such as the U.S. Office of Education to calibrate the readability of "
"all textbooks for the public school system.";
int count_letters = get_letters_count(text_str);
int count_words = get_words_count(text_str);
int count_sentences = get_sentences_count(text_str);;
if ( count_letters > 0
&& count_words > 0
&& count_sentences > 0) {
float L = ((count_letters * 100) / count_words);
float S = ((count_sentences * 100) / count_words);
float grade = 0.0588 * L - 0.296 * S - 15.8;
printf("grade = %.01f\n", grade);
} else {
printf("bad input\n");
}
}
Ouput:
$ gcc main.c && ./a.out
grade = 14.5
Parsing text can be very trick, though.
Once you get a first version working with a known input such as this, try to expand your data set and keep improving your program.
This program is also far from being computationally efficient. If that becomes a bottleneck, you could optimize the functions or maybe reduce the number of loops grouping the functions in a single loop.
Certainly most times it's better to start with a crude working solution and improve from there instead of attempting a more sophisticated/complete solution right from the beginning.
int main() {
int s, b;
int hist[26] = { 0 };
int hist2[26] = { 0 };
char char1, char2;
printf("Hello Netta, enter the letters you would like us to repeat ending with $:\n");
scanf("%c", &char2);
while (char2 != '$') {
if (char2 >= 'A' && char2 <= 'Z')
char2 = char2 + 32;
int char3 = char2 - 'a';
hist2[char3]++;
scanf("%c", &char2);
if (char2 < 0)
break;
}
printf("How many times would you like to loop?\n");
if (!scanf("%d", &s))
return 0;
printf("Enter the string you would like to be checked ending with $:\n");
scanf("%c", &char1);
if (char1 >= 'A' && char1 <= 'Z')
char1 = char1 + 32;
while (char1 != '$' && char1 > 0) {
int char3 = char1 - 'a';
hist[char3]++;
scanf("%c", &char1);
}
for (int i = 0; i < 26; i++) {
if (hist[i] > s * hist2[i]) {
printf("Not enough letters\n");
b = 0;
break;
} else {
b = 1;
}
}
if (b)
printf("Congratulations! you have enough letters to create your song and win the Eurovision!\n");
return 0;
}
//so basically this a homewrok in my university they asked us to do the program input is char and a loop and it compare it with one more input how many times you can loop each letter (no need to check that the input is true but the loop number int
Your program has many problems:
scanf() returns the number of successful conversions. Compare the return value to 1 in your program instead of testing 0, which would never happen for "%c" anyway. Furthermore, the char2 would not be modified if the stream is at end of file.
You must check of char2 is a letter before indexing into the array, otherwise you may access beyond the array boundaries and have undefined behavior.
presentation is important: use proper indentation and spacing for the program to be readable.
include the necessary headers such as <stdio.h>
Here is an improved version:
#include <stdio.h>
int main() {
int hist[26] = { 0 };
int hist2[26] = { 0 };
int s;
char c;
printf("Hello Netta, enter the letters you would like us to repeat ending with $:\n");
while ((scanf("%c", &c) == 1 && c != '$') {
if (c >= 'A' && c <= 'Z')
hist2[c - 'A']++;
else if (c >= 'a' && c <= 'z')
hist2[c - 'a']++;
}
printf("How many times would you like to loop?\n");
if (scanf("%d", &s) != 1)
return 1;
printf("Enter the string you would like to be checked ending with $:\n");
while (scanf("%c", &c) == 1 && c != '$') {
if (c >= 'A' && c <= 'Z')
hist[c - 'A']++;
else if (c >= 'a' && c <= 'z')
hist[c - 'a']++;
}
for (int i = 0; i < 26; i++) {
if (hist[i] > s * hist2[i]) {
printf("Not enough letters\n");
break;
}
}
if (i == 26)
printf("Congratulations! you have enough letters to create your song and win the Eurovision!\n");
return 0;
}
I need to know how to convert a user input, which is a string, to a double. like if he writes in the string "23.45", it converts into double 23.45
(without any library functions).
I already got this code for integer, but don't know how to continue with double:
#include <stdio.h>
void main()
{
char input[100];
printf("Type a String which will be converted to an Integer: ");
scanf("%s", input);
int number = 0;
int i = 0;
if (input[i] >= 48 && input[i] <= 57)
{
while (input[i] >= '0' && input[i] <= '9')
{
number = number * 10;
number = number + input[i] - '0';
i++;
}
printf("string %s -> number %d \n", input, number);
}
else
{
printf("Enter a number! \n");
}
}
There's probably no reason why you'd roll out your own version of this, as strtod in stdlib.h already covers all manner of formats.
Here's a version which covers signed numbers as input and has some hints of where more suitable error handling could be placed:
#include <stdbool.h>
static void halt_and_catch_fire (void);
double strtod_homebrewn (const char* str)
{
double result = 0;
// handle signs:
bool is_negative = false;
if(*str == '-')
{
is_negative = true;
str++;
}
else if(*str == '+')
{
str++;
}
// handle the dot position:
bool is_dot_found = false;
double multiplier = 0.1;
// the actual conversion:
for(const char* s=str; *s!='\0'; s++)
{
if(*s >= '0' && *s <= '9') // ctype.h isdigit() would be preferred here
{
if(is_dot_found)
{
result += (*s - '0') * multiplier;
multiplier /= 10;
}
else
{
result *= 10;
result += *s - '0';
}
}
else if(*s == '.')
{
if(is_dot_found) // two dots?
{
halt_and_catch_fire(); // replace this with error handling
}
is_dot_found = true;
}
else if(*s != '\0') // all cases tested, some weird unknown character found
{
halt_and_catch_fire(); // replace this with error handling
}
}
if(is_negative)
{
result = -result;
}
return result;
}
static void halt_and_catch_fire (void)
{
halt_and_catch_fire();
}
#include <stdio.h>
void main()
{
char input[100];
printf("Type a String which will be converted to a double: ");
scanf("%s", input);
double number = 0.0;
double divider = 1.0;
int inFraction = 0;
int i = 0;
if (input[i] >= 48 && input[i] <= 57)
{
inFraction = 0;
while ((input[i] >= '0' && input[i] <= '9') || input[i] == '.')
{
if (input[i] == '.')
{
i++;
inFraction = 1;
continue;
}
number = number * 10.0;
number = number + input[i] - '0';
i++;
if (inFraction) divider *= 10.0;
}
number /= divider;
printf("string %s -> number %g \n", input, number);
}
else
{
printf("Enter a number! \n");
}
}
Edit: As clux pointed out, this fails when the fraction starts with zeroes. Bummer. Anyway, perhaps someone conceives a simple fix? I can only think of adding a "readzeroes()" function and let that run after the dot.
You already have a function to read an int. Simply use that. Pseudo code:
float read_float()
{
float f = read_int()
if(next is dot) skipdot else return f;
float frac = read_int()
while (frac>1) frac /= 10
return f+frac;
}
Edit: only use this approach for small number of digits after the decimal point.
Read the comments to know why it would fail for a large number of digits.
Since you mentioned without using any library functions, you could do something like this.
float number;
int decimal = 0;
int decimal_found =10;
while(input[i]!='\0')
{
if((input[i] <='0' || input[i] >='9')&&input[i]!='.' )
break;
if(input[i] == '.')
decimal = 1;
if(decimal == 1)
{
number = number + (input[i] - '0')/decimal_found;
decimal_found = decimal_found*10;
}
else
{
number = number *10;
number = number + input[i] - '0';
}
i++;
}
Simply check a decimal variable to know when decimal has been reached, then use and if else to have separate conditions for the number variable
For faster integer input in c we can use http://abhisharlives.blogspot.in/2012/06/really-fast-io-methods-for-programming.html methods ,but how to take faster input of floating numbers in c?
Using ideas from the link you provided, I wrote up my own fast floating number input technique. Here's the code (including the main() to test it):
#include <stdio.h>
#define gc getchar_unlocked
int neg;
int scandigits(double *x, int div) {
register int c = gc();
register double pow10 = 1;
if ( x == NULL ) {
return -1;
}
*x = 0;
while ( (c < '0' || c > '9') && c != '-' ) {
c = gc();
}
if ( c == '-' ) {
neg = 1;
c = gc();
}
while ( !(c < '0' || c > '9') ) {
*x *= 10;
*x += (c - '0');
pow10 *= 10;
c = gc();
}
if ( neg )
*x = -*x;
if ( div )
*x /= pow10;
return c;
}
void scandouble(double *x) {
double left, right = 0;
if ( x == NULL ) {
return;
}
neg = 0;
int ret = scandigits(&left, 0);
if ( ret == '.' )
scandigits(&right, 1);
*x = left + right;
}
int main() {
double d;
scandouble(&d);
printf("%f", d);
return 0;
}
Note: In general, most competitive coding contests do NOT require fast IO. Instead, they require much better algorithms etc. However, in some very rare cases you require fast IO to eke out that last 0.01s to make your code get accepted. I would suggest using such trickery (that's what I call it) in only such cases.
Often using integer math is faster than double. The below uses integer math to compose the significand and optional exponent.
double getdouble(void) {
int ch = getchar();
int sign = ch;
if (sign == '-' || sign == '+') {
ch = getchar();
}
long long sum = 0;
int digit_count = 0;
int dp_offset = -1;
int power10 = 0;
int dp = '.';
for (;;) {
while (ch >= '0' && ch <= '9') {
digit_count++;
if (sum < LLONG_MAX / 10) {
sum = sum * 10 + ch - '0';
} else {
power10++;
}
ch = getchar();
}
if (ch == dp) {
dp = '0';
dp_offset = digit_count;
ch = getchar();
} else {
break;
}
}
if (dp_offset >= 0) {
power10 -= digit_count - dp_offset;
}
if (ch == 'e' || ch == 'E') {
ch = getchar();
int esign = ch;
if (esign == '-' || esign == '+') {
ch = getchar();
}
int expo = 0;
while (ch >= '0' && ch <= '9') {
expo = expo * 10 + ch - '0';
ch = getchar();
}
if (esign == '-') expo = -expo;
power10 += expo;
}
double y = sum;
if (power10) {
// combine these 2 steps for speed with `pow(10,power10)`.
// leave separate for better handling of very large/very tiny numbers
y *= pow(5, power10); //
y = ldexp(y, power10);
}
if (sign == '-') y = -y;
return y;
}
Same answer as atof() 86% of the time.
When in error, 1 bit error.
Worse case: 1 bit.
Handles exponential notation and sub-normals, -0.0.
Does not handle NAN, INF.
Returns INF on +/-DBL_MAX, works on all smaller values.