all.
I've written a program as a solution to Kernighan & Ritchie's exercise 2-3, and its behaviour during testing is (IMHO) wildly unintuitive.
The problem spec says to write a program that converts hex values to their decimal equivalent. The code I've written works fine for smaller hex values, but for larger hex values things get a little... odd. For example, if I input 0x1234 the decimal value 4660 pops out on the other end, which happens to be the correct output (the code also works for letters, i.e. 0x1FC -> 508). If, on the other hand, I were to input a large hex value, say as a specific example 0x123456789ABCDEF, I should get 81985529216486895, though instead I get 81985529216486896 (off by one digit!).
The error in conversion is inconsistent, sometimes with the decimal value being too high and other times too low. Generally, much larger hex values result in more incorrect place values in the decimal output.
Here's my program in its entirety:
/*Kernighan & Ritchie's Exercise 2-3
Write a function 'htoi' which converts a string of hexadecimal digits (including an
optional 0x or 0X) into its equivalent integer value.
*/
#include <stdio.h>
#define MAXLINE 1000 //defines maximum size of a hex input
//FUNCTION DEFINITIONS
signed int htoi(char c); //converts a single hex digit to its decimal value
//BEGIN PROGRAM////////////////////////////////////////////////////////////
main()
{
int i = 0; //counts the length of 'hex' at input
char c; //character buffer
char hex[MAXLINE]; //string from input
int len = 0; //the final value of 'i'
signed int val; //the decimal value of a character stored in 'hex'
double n = 0; //the decimal value of 'hex'
while((c = getchar()) != '\n') //store a string of characters in 'hex'
{
hex[i] = c;
++i;
}
len = i;
hex[i] = '\0'; //turn 'hex' into a string
if((hex[0] == '0') && ((hex[1] == 'x') || (hex[1] == 'X'))) //ignore leading '0x'
{
for(i = 2; i < len; ++i)
{
val = htoi(hex[i]); //call 'htoi'
if(val == -1 ) //test for a non-hex character
{
break;
}
n = 16.0 * n + (double)val; //calculate decimal value of hex from hex[0]->hex[i]
}
}
else
{
for(i = 0; i < len; ++i)
{
val = htoi(hex[i]); //call 'htoi'
if(val == -1) //test for non-hex character
{
break;
}
n = 16.0 * n + (double)val; //calc decimal value of hex for hex[0]->hex[i]
}
}
if(val == -1)
{
printf("\n!!THE STRING FROM INPUT WAS NOT A HEX VALUE!!\n");
}
else
{
printf("\n%s converts to %.0f\n", hex, n);
}
return 0;
}
//FUNCTION DEFINITIONS OUTSIDE OF MAIN()///////////////////////////////////
signed int htoi(char c)
{
signed int val = -1;
if(c >= '0' && c <= '9')
val = c - '0';
else if(c == 'a' || c == 'A')
val = 10;
else if(c == 'b' || c == 'B')
val = 11;
else if(c == 'c' || c == 'C')
val = 12;
else if(c == 'd' || c == 'D')
val = 13;
else if(c == 'e' || c == 'E')
val = 14;
else if(c == 'f' || c == 'F')
val = 15;
else
{
;//'c' was a non-hex character, do nothing and return -1
}
return val;
}
pastebin: http://pastebin.com/LJFfwSN5
Any ideas on what is going on here?
You are probably exceeding the precision with which double can store integers.
My suggestion would be to change your code to use unsigned long long for the result; and also add in a check for overflow here, e.g.:
unsigned long long n = 0;
// ...
if ( n * 16 + val < n )
{
fprintf(stderr, "Number too big.\n");
exit(EXIT_FAILURE);
}
n = n * 16 + val;
My less-than check works because when unsigned integer types overflow they wrap around to zero.
If you want to add more precision than unsigned long long then you will have to get into more advanced techniques (probably beyond the scope of Ch. 2 of K&R but once you've finished the book you could revisit).
NB. You also need to #include <stdlib.h> if you take my suggestion of exit; and don't forget to change %.0f to %llu in your final printf. Also, a safer way to get the input (which K&R covers) is:
int c;
while((c = getchar()) != '\n' && c != EOF)
The first time I ran the code on ideone I got segfault, because I didn't put a newline on the end of the stdin so this loop kept on shoving EOF into hex until it buffer overflowed.
This is a classic example of floating point inaccuracy.
Unlike most of the examples of floating point errors you'll see, this is clearly not about non-binary fractions or very small numbers; in this case, the floating point representation is approximating very big numbers, with the accuracy decreasing the higher you go. The principle is the same as writing "1.6e10" to mean "approximately 16000000000" (I think I counted the zeros right there!), when the actual number might be 16000000001.
You actually run out of accuracy sooner than with an integer of the same size because only part of the width of a floating point variable can be used to represent a whole number.
Related
As part of exercise 2-3 in Ritchie and Kernighan's C programming language, I've written a program that converts hexadecimal inputs into decimal outputs. I want it to be able to handle larger numbers, but it seems to be doing integer arithmetic somewhere. When you enter something like "DECAFCAB" it spits out a large negative int. I figured out that I need to add the "LL" suffix to my literals, which I did, but it's still not working. Any help please? Sorry if this is a dumb question or a typo, but I've been at it for an hour and can't figure it out. :(
#include <stdio.h>
#define MAX_LINE 1000
void getline(char s[])
{
int i;
char c;
for(i = 0; i < MAX_LINE-1 && (c=getchar()) != EOF && c != '\n'; ++i)
s[i] = c;
s[i] = '\0';
printf("\n%s", s);
}
long long htoi(char s[]) // convert the hex string to dec
{
long long n = 0;
int i = 0;
if(s[i] == '0') // eat optional leading Ox or OX
++i;
if(s[i] == 'x' || s[i] == 'X')
++i;
while(s[i] != '\0')
{
if((s[i] >= '0' && s[i] <= '9'))
n = 16LL * n + (s[i] - '0'); // here is the arithmetic in question
else if(s[i] >= 'A' && s[i]<= 'F')
n = 16LL * n + (s[i] - 'A' + 10LL);
else if(s[i] >= 'a' && s[i] <= 'f')
n = 16LL * n + (s[i] - 'a' + 10LL);
else {
printf("\nError: Encountered a non-hexadecimal format: the '%c' character was unexpected.", s[i]);
printf("\nHexadecimal numbers can begin with an optional 0x or 0X only, and contain 0-9, A-F, and a-f.\n\n");
return -1;
}
++i;
}
return n;
}
main()
{
char input[MAX_LINE];
long long hex_output;
while(1){
getline(input);
hex_output = htoi(input);
if(hex_output >= 0)
printf("\nThe value of the hexadecimal %s is %d in decimal.\n\n", input, hex_output);
}
}
You told printf to expect an int when you made the placeholder %d. To make it expect (and therefore read the entirety of a) long long, modify it to %lld.
The reason it looks like a plain int is that with varargs functions like printf, it doesn't know what the argument sizes are, and the format string is the only way to figure it out. When you say to expect plain int, it reads sizeof(int) bytes from the argument, not sizeof(long long) bytes (it's not necessarily byte-oriented, but that's how much data is read), and (on a little endian system with 4 byte int and 8 byte long long) you see (roughly) the result of the argument with the top 4 bytes masked off.
The problem you are experiencing comes from treating a (conventionally) "unsigned" hexadecimal integer value as "signed". Resorting using to a larger built-in data type will get you past the problem with going from 31 to 32 bits, but this masks the actual problem. (If you extend to 64 bits, you will encounter the same problem and be back asking, "why doesn't this work.")
Better is to write code that doesn't require ever wider registers. There will always be a maximum width, but the answer to this OP is to use an "unsigned long".
#include <stdio.h>
unsigned long htoi( char s[] ) { // convert the hex string to dec
unsigned long n = 0;
int i = 0;
if(s[i] == '0') // eat optional leading Ox or OX
++i;
if(s[i] == 'x' || s[i] == 'X')
++i;
for( ; s[i]; i++ ) {
unsigned int dVal = 0; // don't copy/paste complex statements.
if((s[i] >= '0' && s[i] <= '9'))
dVal = s[i] - '0'; // simple
else if(s[i] >= 'A' && s[i]<= 'F')
dVal = s[i] - 'A' + 10; // simple
else if(s[i] >= 'a' && s[i] <= 'f')
dVal = s[i] - 'a' + 10; // simple
else {
// less verbose
printf("\nError: '%c' unexpected.", s[i] );
return 0; // NB: Notice change!!
}
n = (16 * n) + dVal; // simple...
}
return n;
}
int main() {
// simplified, stripping out user input.
char *hexStr = "0xDECAFCAB";
unsigned long hex_output = htoi( hexStr );
// Notice the format specifier to print an ordinary (unsigned) long
printf( "\nThe value of the hexadecimal %s is %u in decimal.\n\n", hexStr, hex_output );
return 0;
}
The value of the hexadecimal 0xDECAFCAB is 3737844907 in decimal.
When K&R wrote the original book, there was no such thing as "long long", but there was "unsigned long".
I'm having trouble in converting numbers to float using getchar() method to solve my problem. For my problem I need to store characters in an array of fixed size = 50. Also, storing in an array only happens when there is a space ' ' or a newline \n read using getchar(). This happens until EOF is read. At last, the float number and it's double (with a tab space) is returned and printed using printf.
As per instructions, only getchar() is allowed. Functions like scanf(), fgets(), atoi(), atol(), atof(), strtol(), strtoul() or an extra array can not be used.
Here is what I've come up with till now. ( see sample input and output at bottom )
#include <stdio.h>
#define SIZE 50 // assume no more than 50 literals in input
int main(){
float c;
float u;
float value = 0.0;
float resu[SIZE];
int index = 0;
int i;
char sub = '0';
value = 0;
c = getchar();
while ( c != EOF){
if(c == '.'){
u = 0.1;
}
else if (c == ' ' || c == '\n'){
if(u == 0.1){
value = value * 0.1;
}
resu[index] = value;
index++;
value = 0;
}
if( c >= '0' && c <= '9'){
value = value * 10 + (c-sub);
}
c = getchar(); // read next
}
//printing the result
for(i=0; i < index; i++)
printf("%.4lf \t %.4lf\n", resu[i],resu[i] *2.0);
return 0;
}
(Note- There is a tab between the original number and it's double)
Sample Input:
2.3 4.56
43.3 43 5.3
.3 1.2
Sample Output:
2.3000 4.6000
45.6000 91.2000 //ERROR
43.3000 86.6000
4.3000 8.6000 //ERROR
5.3000 10.6000
0.3000 0.6000
1.2000 2.4000
Two things you don't do is initialize u or reset u for each word.
float u = 0;
....
else if (c == ' ' || c == '\n') {
if (u == 0.1){
value = value * 0.1;
}
resu[index] = value;
index++;
value = 0;
u = 0; // Reset it for next number
}
Also, you hard-code u = 0.1, but that only works when there is only 1 decimal place. That may be ok for this assignment, but a better option would be to count the digits after the decimal.
#include <stdbool.h>
#include <math.h>
#include <ctype.h>
...
int digits_after_decimal = 0;
bool have_decimal_point = false;
int value = 0;
int c;
while ((c = getchar()) != EOF) {
// Decimal point found?
if ('.' == c) {
have_decimal_point = true;
}
else if (isdigit(c)) {
// Add this digit to integer value
// Parentheses not required but added for clarity
value = (value * 10) + (c - '0');
// If decimal point already found, increment count
if (have_decimal_point) digits_after_decimal += 1;
}
// Complete word. Save and reset
// TODO: Multiple spaces between words?
else if (' ' == c || '\n' == c) {
// Divide by the correct power of 10 based on
// the number of digits after the decimal point
resu[index++] = value / pow(10, digits_after_decimal);
if (index == SIZE) break; // Avoid overflow
// Reset for next number
digits_after_decimal = 0;
have_decimal_point = false;
value = 0;
}
// TODO: Negative numbers?
}
Here's a question from the last year's first "Intro to programming" exam at my uni:
Using the getchar() function read an input sequence consisting of
numbers, + and - signs. The output should be the result of those
arithmetical operations.
For example, if the input is 10+13-12+25-5+100, the output should be 131.
Now, given that I have a little bit of C experience before attending uni, this problem seems easy to solve using pointers, arrays, etc.
But here's the catch: on the exam you can only use things that the students were taught so far. And given that this exam is only like a month after the start of the school year, your options are fairly limited.
You can only use variables, basic input/output stuff, operators (logical and bitwise), conditional statements and loops, functions.
That means no: arrays, strings, pointers, recursion, structures, or basically any other stuff that makes this easy.
How in the hell do I do this? Today is the second time I've spent 3 hours trying to solve this. I have solved it successfully, but only after "cheating" and using arrays, string functions (strtol), and pointers. It's important for me to know how to solve it by the rules, as I'll have similar stuff on the upcoming exam.
Edit: my attempts so far have amounted to using the while loop combined with getchar() for input, after which I just get stuck. I don't have the slightest idea of what I should do without using more "tools".
The solution is quite simple, but it might not be obvious for a beginner. I will not provide a complete program, but rather outline the steps needed to implement this with only a few variables.
First of all, it's important to notice two things:
Your input can only contain one of -, + or any digit (0123456789).
The getchar() function will read one character of input at a time, and will return EOF when the end of the input is reached or an error occurs.
Now, onto the solution:
Start by reading one character at a time, in a loop. You will only stop if you reach end of input or if an error occurs:
int c;
while ((c = getchar()) != EOF) {
// logic here
}
Start with an accumulator set to 0, and "add" digits to it every time you encounter a digit.
// outside the loop
int acc = 0;
// inside the loop
if (/* c is a digit */)
acc = acc * 10 + (c = '0');
Hint: that /* c is a digit */ condition might not be simple, you can put this in the else of the check for - and +.
Every time you encounter either - or +, remember the operation, and each time you encounter an operator, first perform the previous operation and reset the accumulator.
// outside the loop
int op = 0;
int result = 0;
// inside the loop
if (c == '+' || c == '-') {
if (op) {
// there already is a previous operation to complete, do it
if (op == '+')
result += acc;
else
result -= acc;
} else {
// first operation encountered, don't do anything yet
result = acc;
}
acc = 0; // reset
op = c; // remember the current operation for the future
}
When you reach the end of the input (i.e. you exit the loop), perform the last operation (same logic inside the if from point 3).
Output the result:
You would normally write something like:
printf("%d\n", result);
However, if you cannot use string literals ("%d\n") or the printf() function, you will have to do so manually using putchar(). This is basically the opposite of what we did before to scan numbers into an accumulator.
Print the sign first if needed, and make the value positive:
if (result < 0) {
putchar('-');
result = -result;
}
Find the maximum power of 10 that is lower than your number:
int div = 1;
while (result / div / 10)
div *= 10;
Use the power to extract and print each digit by division and modulo by 10:
while (div) {
putchar('0' + ((result / div) % 10));
div /= 10;
}
Note: the '0' + at the beginning is used to convert digits (from 0 to 10) to the relative ASCII character.
End with a newline:
putchar('\n');
When writing a parser, I typically find myself that I "buffer" the next operation that "will be done". When the input changes state - you are reading digits, but then you read an operation - then you execute the "buffered" action and buffer the next operation that will be done in the future.
Example:
10+13-12
^^ - we read 10
^ - result=10 - we buffer that we *will* have to do + in the future
^^ - reading 13
^ - och we stopped reading numbers!
we execute _buffered_ operation `+` , so we do result += 13
and buffer `-` to be done in the future
^^ - we read 12
^ - och, EOF! we execute buffered operation `-` , so we do result -= 12
- etc.
The code:
#include <stdio.h>
int main() {
int result = 0; // represents current result
int temp = 0; // the temporary number that we read into
int op = 0; // represents the _next_ operation that _will_ be done
while (1) {
int c = getchar();
switch (c) {
// we read an operation, so we should calculate _the previous_ operation
// or this is end of our string
case '+': case '-': case EOF:
if (op == 0) {
// we have nothing so far, so start with first number
result = temp;
} else if (op == '+') {
result += temp;
} else if (op == '-') {
result -= temp;
}
// the next operation we will do in future is stored in op
op = c;
// current number starts from 0
temp = 0;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
// read a digit - increment temporary number
temp *= 10;
temp += c - '0';
break;
}
// we quit here, the result for last operation is calculated above
if (c == EOF) {
break;
}
}
printf("%d\n", result);
// As I see it was mentioned that "%d\n" is a string,
// here's the simplest algorithm for printing digits in a number.
// Extract one digit from the greatest position and continue up
// to the last digit in a number.
// Take negative numbers and throw them out the window.
if (result < 0) {
putchar('-');
result = -result;
}
// Our program currently supports printing numbers up to 10000.
int divisor = 10000;
// 000100 should print as 100 - we need to remember we printed non-zero
int was_not_zero = 0;
while (divisor != 0) {
// extract one digit at position from divisor
int digit = result / divisor % 10;
// if the digit is not zero, or
// we already printed anything
if (digit != 0 || was_not_zero) {
// print the digit
putchar(digit + '0');
was_not_zero = 1;
}
// the next digit will be to the right
divisor /= 10;
}
putchar('\n');
}
#include <string.h>
#include <stdio.h>
void operate(int * sum, int * n, char todo) {
if (todo == 1) *sum += *n;
else if (todo == -1) *sum -= *n;
printf("%s %d\n", todo == 1 ? "ADD :" : "SUB :", *n);
*n = 0;
}
int main()
{
char * problem = "10+13-12+25-5+100";
int len = strlen(problem);
int i=0;
char c;
int n = 0;
int sum = 0;
char todo = 1;
while(i < len)
{
c = problem[i++];
if (c < 48 || c >= 58)
{
// Adds or subtracts previous and prepare next
operate(&sum, &n, todo);
if (c == '+') todo = 1;
else if (c == '-') todo = -1;
}
else
{
// Collects an integer
if (n) n *= 10;
n += c - 48;
}
}
operate(&sum, &n, todo); // Last pass
printf("SUM => %d\n", sum); // => 131
return 0;
}
#include <stdio.h>
void do_operation(char next_operation, int * result, int * number){
if (next_operation == '+'){
*result += *number;
*number = 0;
} else if (next_operation == '-'){
*result -= *number;
*number = 0;
} else {
printf("Unknown operation error.");
}
}
int main(int argc, char *argv[]){
char c;
int number = 0;
int result = 0;
char next_operation = '+';
do {
c = getchar();
if( c >= '0' && c <= '9' ){
number = number * 10 + c - 48;
} else if (c == '+'){
do_operation(next_operation, &result, &number);
next_operation = '+';
} else if (c == '-'){
do_operation(next_operation, &result, &number);
next_operation = '-';
} else {
do_operation(next_operation, &result, &number);
}
} while (c != '\n');
printf("%d", result);
}
#include <stdio.h>
#include <stdlib.h>
#include<limits.h>
int getInt() {
int n, check;
char ch;
do {
fflush(stdin);
printf("Enter positive integer n = ");
check = scanf("%d%c", &n, &ch);
if ((check == 2)&&(ch == '\n')) {
if (n > INT_MAX) {
printf("Invalid number\n");
} else if (n < 0) {
printf("n must > 0");
} else {
break;
}
} else printf("Invalid number\n");
} while (1);
return n;
}
int main(int argc, char** argv) {
int n;
n = getInt();
}
My code accepts user input number in range 0 to INT_MAX.
When I input -1, program displays "n must > 0".
But when I input '77777777777777777777777777777777' (> INT_MAX) program still displays "n must > 0", and not 'Invalid number'.
With out of range input in scanf("%d%c", &n, &ch);, the behavior is undefined.
Instead read a line of input with fgets(), then quality using strtol()
for (;;) {
char buf[100];
if (fgets(buf, sizeof buf, stdin) == NULL) {
printf("No more input\n");
return -1;
}
errno = 0;
char *endptr;
long val = strtol(buf, &endptr, 0);
// No numeric conversion done at all?
// Numeric input outside long range?
// Junk after the numeric text?
if (buf == endptr || errno == ERANGE || *endptr != '\n') {
printf("Invalid number\n");
continue;
}
// Outside int range?
if (val < INT_MIN || val > INT_MAX) {
printf("Invalid number\n");
continue;
}
if (val < 0) {
printf("n must > 0");
continue;
}
n = (int) val;
}
I'd recommend a re-usable int get_int(int *val) helper function.
First you need to understand how variables stores data.
In 64 bits architeture, the int type have 4 bytes (either the C long type, regardless architeture), so can store the following values:
00000000 00000000 00000000 00000000 = 0 (decimal value)
01111111 11111111 11111111 11111111 = 2,147,483,647 (decimal value)
11111111 11111111 11111111 11111111 = 4,294,967,294 (unsigned decimal value)
11111111 11111111 11111111 11111111 = -1 (signed decimal value)
Note that the integer types can use the Most Significant Bit (MSB) to represent signal (0 to positive, 1 to negative) using a modular aritmetic.
For more details about integer signal: https://en.wikipedia.org/wiki/Two%27s_complement
So, to store decimal data higher than INT_MAX, you need more bytes than you have using int type. A good way, compatible with 64 bits architeture, is using long long type.
Long long type uses 8 bytes, so can stores value higher than INT_MAX.
You will have to declare:
long long n;
And use scanf() like this:
scanf("%lld%c", &n, &ch);
Your fflush(stdin) have to be after the scanf(), because if your aplication break the loop after the scanf() and before have reached the fflush() instruction you may have problem in further input handling. Like this:
check = scanf("%lld%c", &n, &ch);
fflush(stdin);
However, some developers disapprove use fflush() in stdin, so this is an alternative (a bit more complex) using getch() accepting only numbers and converting char* to long long using strtoll():
char c = 0;
char* input_number = malloc(32);
int accepted_chars = 0;
memset(input_number, 0, 32);
while(c != '\r'){ //loop until user press ENTER
c = getch();
//receive numbers keys pressed
if(c >= '0' && c <= '9'){
*(input_number + accepted_chars) = c;
accepted_chars ++;
printf("%c", c);
}
//receive backspace key pressed
if(c == 8){
if(accepted_chars > 0){ //don't do nothing if there is nothing to clear
accepted_chars --;
*(input_number + accepted_chars) = 0;
printf("\b");
printf(" ");
printf("\b");
}
}
}
printf("\n");
char* endptr;
n = strtoll(input_number, &endptr, 10); //convert string in base 10 (decimal) long long type
Just checking
int x;
...
if (x > INT_MAX) { ...
is something that will never work. If an int value cannot have values above INT_MAX then it is impossible that you can ever get x stored a value higher than that. So that if will become always false and the compiler will probably eliminate all the code you put inside that if's then block.
Normal reading routines like scanf() actually limit the input to a value in the range of allowable values.
But if you want to read and build the number yourself, you need to ancitipate the possibility, and use better triggers to stop.
For example, checking that the number is above (INT_MAX - 9) / 10 will tell you that if you try to add another digit to it, you'll run the risk of overflowing the integer, when you add the digit. You can simply stop there and don't continue reading, but if you want to read one more digit (who knows, it could be a 0 and that doesn't hurt, you'll have to check for something like
int digit;
int the_integer = 0;
while ( (digit = fgetchar(stdin)) != EOF
&& isdigit(digit)
&& the_integer <= (INT_MAX - (digit - '0')))
{
the_integer *= 10;
the_integer += digit;
} /* while */
if (digit != EOF && isdigit(digit)) {
/* x > (INT_MAX - (digit - '0')) */
/* this is necessary, as if you don't do it, you'll lose
* the next character to read. */
unput(digit, stdin);
}
This way you'll check the number x before multiplying it by 10 and adding the digit - '0' value.
As pointed out in the comments section, there is no point in testing if n > INT_MAX, as n is of type int and therefore by definition unable to represent any value larger than INT_MAX.
A simple, flexible way of checking the size of a number provided as user input would be to read it in as a string and count the number of digits by using the function strlen and possibly also isdigit to count the actual digits.
However, when dealing with potentially large numerical values as user input, it is usually better to first read this input into a data type that is larger than int and do your range checks with this larger data type. After you are satisfied that the number is in the desired range, you can convert it to the smaller int data type. Although the ISO C standard does not guarantee that a long long is larger than an int, this is a reasonable assumption and it is the case on all compilers that I am aware of. However, on some platforms (including 64-bit Windows), the data type long is the same size as int, so a long cannot be reliably used for this on all platforms.
Probably the easiest solution to your problem would be to use the function strtol or strtoll. Using these functions has the advantage that you don't need a larger data type and it explicitly tells you if a number is out of range, by setting errno to ERANGE. However, these functions only support the data types long and long long, but not int. Therefore, if you want to use int, you will have to do your range checks manually before you convert it.
I am doing the exercise in the C Programming language book, and exercise 2-3 asked us to write a function htoi to convert a hexadecimal number to decimal number.
This is the code I wrote, however when it runs, it always show that my hexadecimal number is illegal.
Please help!
#include<stdio.h>
#define TRUE 1
#define FALSE 0
int htoi (char s[]);
int main() {
printf("The decimal number is %d\n", htoi("0x134"));
return 0;
}
int htoi (char s[]) {
int j; /* counter for the string */
int temp; /* temp number in between conversion */
int number; /* the converted number */
int ishex; /* if the number is a valid hexadecimal number */
char c;
number = 0;
temp = 0;
ishex = FALSE;
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
ishex = TRUE;
}
else {
ishex = FALSE;
printf("This is not valid hexadecimal number.\n");
return number = 0;
}
if (ishex == TRUE) {
for (j = 2; (c = s[j]) != EOF; ++j) {
if (c >= '0' && c <= '9')
temp = c - '0';
else if (c >= 'a' && c <= 'f')
temp = 10 + c - 'a';
else if (c >= 'A' && c <= 'F')
temp = 10 + c - 'A';
else {
printf("This is a illegal hexadecimal number.\n");
ishex = FALSE;
return 0;
}
number = number * 16 + temp;
}
}
return number;
}
A string is a sequence of characters that terminates at the first '\0' character. That means "0x134" terminates with a '\0' character value, not an EOF value.
You are operating on a sequence of characters that you expect to be terminated by an EOF value, but that is simply not possible. I'll explain why later... Suffice to say for now, the string "0x134" contains no EOF value.
Your loop reaches the string-terminating '\0', which isn't in the range 0..9, a..f or A..F and so this branch executes:
else {
printf("This is a illegal hexadecimal number.\n");
ishex = FALSE;
return 0;
}
Perhaps you meant to write your loop like so:
for (j = 2; (c = s[j]) != '\0'; ++j) {
/* SNIP */
}
I promised to explain what is wrong with expecting EOF to exist as a character value. Assuming an unsigned char is 8 bits, getchar can return one of 256 character values, and it will return them as a positive unsigned char value... OR it can return the negative int value EOF, corresponding to an error or end-of-file.
Confused? In an empty file, there are no characters... Yet if you try to read a character from the file, you will get EOF every time, in spite of there being no characters. Hence, EOF is not a character value. It's an int value, and should be treated as such before you attempt to convert the value to a character, like so:
int c = getchar();
if (c == EOF) {
/* Here, c is NOT A CHARACTER VALUE! *
* It's more like an error code ... *
* XXX: Break or return or something */
}
else {
/* Here, c IS a character value, ... *
* so the following conversion is ok */
char ch = c;
}
On another note, c >= '0' && c <= '9' will evaluate truthfully when c is one of the digits in the range 0..9... This is a requirement from the C standard
Neither c >= 'a' && c <= 'f' nor c >= 'A' && c <= 'F' are required to evaluate truthfully under any circumstance, however. It happens to work on your system, because you are using ASCII which contains all of the lowercase letters in one contiguous block, and all of the uppercase letters in another contiguous block. C does not require that ASCII be the character set.
If you want this code to work portably, you might consider something like:
char alpha_digit[] = "aAbBcCdDeEfF";
if (c >= '0' && c <= '9') {
c -= '0';
}
else if (strchr(alpha_digit, c)) {
c = 10 + (strchr(alpha_digit, c) - alpha_digit) / 2;
}
else {
/* SNIP... XXX invalid digit */
}