`
#include <unistd.h>
int ft_atoi(char *str)
{
int c;
int sign;
int result;
c = 0;
sign = 1;
result = 0;
while ((str[c] >= '\t' && str[c] <= '\r') || str[c] == ' ')
{
c++;
}
while (str[c] == '+' || str[c] == '-')
{
if (str[c] == '-')
sign *= -1;
c++;
}
while (str[c] >= '0' && str[c] <= '9')
{
result = (str[c] - '0') + (result * 10);
c++;
}
return (result * sign);
}
#include <stdio.h>
int main(void)
{
char *s = " ---+--+1234ab567";
printf("%d", ft_atoi(s));
}
`
This line: result = (str[c] - '0') + (result * 10);
Why do we subtract zero and multiply by 10? How its convert ascii to int with this operations?
Thanks...
Some detail before answering your question
Internally everything is a number a char is not an exception.
In C char is a promoted type of integer meaning characters are integer in C. The char which is promoted type of integer are mapped to responding ASCII Value.
For example:
Capital Letter Range
65 => 'A' to 90 => 'Z'
Small Letter Range
97 => 'a' to 122 => 'z'
Number Range
48 => '0' to 57 => '9'
To answer your question
The ASCII CHARACTER '0' subtracted from any ASCII CHARACTER that is a digit(0-9) results to an actual Integer.
For Example
'9'(57) - '0'(48) = 9 (int)
'8'(56) - '0'(48) = 8 (int)
Remember char are promoted integer in C Read the detail to understand this statement.
And Also the ASCII CHARACTER '0' added to any INTEGER in the range(0-9) results to an ASCII CHARACTER.
For Example
9 + '0'(48) = '9'(57) (char)
8 + '0'(48) = '8' (56)(char)
Please see ASCII table
The ASCII code for '0' is 48 - not zero. Therefore, to convert to decimal you need to subtract 48
Related
I have an input data of 4 character string (alphanumeric) or 3 character string and I need to convert these ASCII character string to unique float in 2 digits each, separated by decimal.
Ex:
Input string = 5405, output data = 54.05
Input string = 53BC, output data = 53.199 ( B ascii value is ~ 0x42 in hex and C is 0x43 )
Issue is I am seeing the same output when input strings are 560B and 5618, as both results in same output as 56.18.
Is there a way to uniquely generate a float number in these cases?
Max value of float allowed is 99.999.
Simple math tells us that this is not possible. The number of unique alphanumeric strings of length 4 (case-insensitive) is 36^4 = 1,679,616 while the number of non-negative unique floating point numbers with at most 3 fractional digits and less than 100 is 10^5 = 100,000.
If the string were restricted to hexadecimal digits, there would only be 16^4 = 65,536 possibilities in which case a unique encoding would be possible.
Slightly off-topic: when a mapping is needed into a domain which is too small to accommodate the result of a unique mapping, a hash function is the "standard tool", but collisions must be handled.
Your encoding is somewhat confusing, but here is a simple solution:
use 2 digits for the integral part
use 2 digits for fractional parts 00 to 99
use a combination of 1 letter and 1 letter or digit for fractional parts 100 to 999. There are 26*36 = 936 such combinations, enough to cover the 900 possibilities.
all values from 00.00 to 99.999 can be encoded.
some 4 letter and digit combinations are not used.
the encoding is not unique. eg: 53A0 is 53.100, the same number as 53.10 encoded as 5310.
Here is an implementation:
#include <stdib.h>
double fdecode(const char *s) {
char a[7];
a[0] = s[0];
a[1] = s[1];
a[2] = '.';
if (s[2] >= '0' && s[2] <= '9') {
a[3] = s[3];
a[4] = s[4];
a[5] = '\0';
} else {
// assuming uppercase letters
int n = 100 + (s[3] - 'A') * 36;
if (s[4] >= '0' && s[4] <= '9') {
n += s[4] - '0';
} else {
n += 10 + (s[4] - 'A') % 26;
}
snprintf(&a[3], 4, "%d", n);
}
return strtod(a, NULL);
}
int fencode(char *s, double d) {
char a[7];
if (d >= 0 && snprintf(a, 7, "%06.3f", d) == 6) {
s[0] = a[0];
s[1] = a[1];
if (a[5] == '0') {
s[2] = a[3];
s[3] = a[4];
} else {
int n = atoi(a + 3);
s[2] = 'A' + (n / 36);
n %= 36;
if (n < 10) {
s[3] = '0' + n;
} else {
s[3] = 'A' + n - 10;
}
}
s[4] = '\0';
return 4;
} else {
s[0] = '\0';
return -1;
}
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
This is for C programming. I am studying for a midterm and one of the problems on the practice test had me a little confused and was hoping someone could help walk me through the code.
The code to the function is:
int xtoi(char s[])
{
int i;
int result;
i = result = 0;
while (s[i]) {
if (s[i] >= '0' && s[i] <= '9') {
result = result * 16 + s[i++] - '0';
} else if (s[i >= 'a' && s[i] <= 'f') {
result = result * 16 + s[i++] - 'a' + 10;
} else if (s[i] >= 'A' && s[i] <= 'F') {
result = result * 16 + s[i++] - 'A' +10;
}
}
return result;
}
What is the result of the following?
xtoi("1fa")
The answer should be 506, but I have no idea how. It is probably quite simple but this is all relatively new to me, so any help and guidance is much appreciated.
Thanks in advance.
If s[i] is a char from '0' to '9', convert it to the
corresponding int(0 to 9).
If not, if s[i] is a char from 'a' to 'f', convert it to the corresponding int(10 to 16).
If not, if s[i] is a char from 'A' to 'F', convert it to the corresponding int(10 to 16).
If still not, ignore it.
Then all such numbers are added up (following a rule) to generate the hex value represented by s.
Hint concerning the rule: For example, say you want to get a decimal number consists of '4' and '2'. You'll let a temporary result to be 4 first, multiply it by 10, and add 2 to that temp result. This will give you what you want: 4 * 10 + 2 = 42.
Please think about it again, and I promise you can understand it on your own.
BTW, the result of xtoi("1fa") is identical to that of strtol("1fa", NULL, 16)
The function converts hex strings into ints. It is analagous to atoi.
int xtoi(char s[])
{
int i = 0;
int result = 0;
/* while we haven't seen the null terminator */
while (s[i]) {
/* if this char is a digit
* convert this char to an int and then add
* it to the next decimal place in the number.
* Then advance to the next character */
if (s[i] >= '0' && s[i] <= '9')
result = result * 16 + s[i++] - '0';
/* if this character is a hex digit
* convert this char to an int and then add
* it to the next decimal place in the number.
* Then advance to the next character */
else if (s[i] >= 'a' && s[i] <= 'f')
result = result * 16 + s[i++] - 'a' + 10;
/* same as above, except for uppercase hex digits */
else if (s[i] >= 'A' && s[i] <= 'F')
result = result * 16 + s[i++] - 'A' + 10;
}
/* return the converted number */
return result;
}
This program could be rewritten like this
int xtoi(char s[])
{
int i = 0;
int result = 0;
/* while we haven't seen the null terminator */
for (i = 0; s[i]; ++i) {
/* if this char is a digit
* convert this char to an int and then add
* it to the next decimal place in the number. */
if (isdigit(s[i]))
result = result * 16 + (s[i] - '0');
/* if this character is a hex digit
* convert this char to an int and then add
* it to the next decimal place in the number. */
else if (isxdigit(s[i])) {
/* if the hex digit is uppercase, the char to subtract
* from is also uppercase. Otherwise it is lowercase */
char base = isupper(s[i]) ? 'A' : 'a';
result = result * 16 + s[i] - base + 10;
}
}
/* return the converted number */
return result;
}
The output of the new xtoi("1fa"); is correct: 506.
I am new to C and I was looking for a custom function in C that would convert a string to an integer and I came across this algorithm which makes perfect sense except for one part. What exactly is the -'0' doing on this line n = n * 10 + a[c] - '0';?
int toString(char a[]) {
int c, sign, offset, n;
if (a[0] == '-') { // Handle negative integers
sign = -1;
}
if (sign == -1) { // Set starting position to convert
offset = 1;
}
else {
offset = 0;
}
n = 0;
for (c = offset; a[c] != '\0'; c++) {
n = n * 10 + a[c] - '0';
}
if (sign == -1) {
n = -n;
}
return n;
}
The algorithm did not have an explanation from where I found it, here.
The reason subtracting '0' works is that character code points for decimal digits are arranged sequentially starting from '0' up, without gaps. In other words, the character code for '5' is greater than the character code for '0' by 5; character code for '6' is greater than the character code for '0' by 6, and so on. Therefore, subtracting the code of zero '0' from a code of another digit produces the value of the corresponding digit.
This arrangement is correct for ASCII codes, EBSDIC, UNICODE codes of decimal digits, and so on. For ASCII codes, the numeric codes look like this:
'0' 48
'1' 49
'2' 50
'3' 51
'4' 52
'5' 53
'6' 54
'7' 55
'8' 56
'9' 57
Assuming x has a value in the range between '0' and '9', x - '0' yields a value between 0 and 9. So x - '0' basically converts a decimal digits character constant to its numerical integer value (e.g., '5' to 5).
C says '0' to '9' are implementation defined values but C also guarantees '0' to '9' to be sequential values.
My program's goal is to receive from the standard input a text, and a 'number', and to return to the screen the text after shifting of the letters 'number' times (for shift 3, 'a' becomes 'd', 'e' becomes 'g'). It should only shift the lower case letter and should be cyclic (letter 'y' in a shift of 3 should become 'a' again).
I have some bugs, though. If I shift 't' 11 times, it'll come to 'e', but if I shift 't' 12 times, I get space (" "). Why? Here's my code:
3 int main(int argc, char *argv[]) {
4 if (argc != 2) {
5 printf ("Wrong input\n");
6 return 1;
7 }
8 int shift = atoi (argv[1]);
9 int c;
10 while ((c = getchar()) != EOF) {
11 if (c >= 'a' && c <= 'z') {
12 char newch = c + shift;
13 if (newch > 'z') {
14 newch = 'a' + (newch - 'z' - 1);
15 }
16 if (newch < 'a') {
17 newch = 'z' - ('a' - newch - 1);
18 }
19 putchar (newch);
20 }
21 else
22 putchar(c);
23 }
24 return 0;
25 }
Also, when compiling I receive those warnings:
shift_chars.c: In function `main':
shift_chars.c:8: warning: implicit declaration of function `atoi'
shift_chars.c:8: warning: ISO C90 forbids mixed declarations and code
What do those mean?
The first warning means you did not include <stdlib.h>, which is where atoi() is declared.
The second warning means that you declared variables (shift etc) after you had some executable code. C++ allows that; C99 allows that; ISO C 90 (ANSI C 89) did not allow that. In older C, all variables in a block had to be defined before any executable statements.
Also, if you are shifting 3 and translate 'y' to 'a' you have a bug -- it should be 'b' ('z', 'a', 'b').
One of your problems is that the code for 'z' is 122, and adding 11 to 122 wraps you to a negative value if your characters (char type) are signed; you do not have this problem if char is unsigned. You'd probably do best to use an int instead of char while computing the new character value.
You might find it easier to manage if you calculate:
int newch = ((c - 'a') + shift) % 26 + 'a';
The c - 'a' gives you an index into the alphabet: a = 0, b = 1, ... z = 25. Add the shift; take the result modulo 26 to get the offset into the alphabet again; then add the result to 'a' to get the correct letter. Since c - 'a' generates an integer (because all char values are promoted to int), this avoid any overflows in char.
Indeed, you could avoid newch altogether and simply compute:
a = (c - 'a' + shift) % 26 + 'a';
You can then omit the putchar() at line 19, and drop the else, leaving the putchar(c); to output both transformed and untransformed characters.
I would shift the chars this way:
if(c >= 'a' && c <= 'z') {
newchar = (c - 'a' + shift) % 26 + 'a';
}
As for warnings - guys perfectly explained them.
If you look at the ascii table, 't' has the value 116. A char is likely signed on your platform.
So you get 116 + 12 = 128 , however that will be represented using a signed 8 bit value, so the value will be -128. Then you end up calculating newch = 'z' - ('a' - newch - 1);
If you use int newch = c + shift; , you'll not have that problem (until the addition wraps past 2147483648).
shift_chars.c:8: warning: implicit declaration of function 'atoi' says that the compiler has not seen a declaration for the atoi() function. You should #include <stdlib.h> to use atoi
Instead of lines 12 through 18, just do
char newch = (c - 'a' + shift) % 26 + 'a';
try to change
char newch = c + shift;
to
int newch = c + shift;
or, alternatively to unsigned char
My answer include explanation, plus solution:
Your newch has a char datatype. Most machines interpretes char as 1 byte.
That would mean the maximum decimal value for your newch variable is:
11111111 (in binary) == 128 in decimal. That's 0-127.
Now, in your code, when you pressed 't', getchar get this as 116 in decimal. If you add it with 12 (your shift), then the result will be 116+12=128. 128 overflows from the 0-127 range.
The solution? Make your newch datatype able to accept greater range, like:
unsigned char newch = c + shift;
or
int newch = c + shift;
Here's the complete code:
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
printf ("Wrong input\n");
return 1;
}
int shift = atoi (argv[1]);
int c;
while ((c = getchar()) != EOF) {
if (c >= 'a' && c <= 'z') {
unsigned char newch = c + shift;
if (newch > 'z') {
newch = 'a' + (newch - 'z' - 1);
}
if (newch < 'a') {
newch = 'z' - ('a' - newch - 1);
}
putchar (newch);
}
else
putchar(c);
}
return 0;
}
I.E., you enter the number 5, and the character A and the output would yield F. I have no idea how to even start to go about this, any give me a push in the right direction?
Individual characters are represented by numbers according to the ASCII code (usually). In C, if you add a number to a character, you're shifting the character down. Try:
char c = 'A';
int n = 5;
printf("%c\n", c + n);
Look at the ASCII table and note the values of the characters.
Try this:
#include <stdio.h>
char shift_char(char val, char shift)
{
val = toupper(val);
assert(isupper(val));
char arr[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return arr[ ( (toupper(val) - 'A' + shift) % 26) ];
}
You can get a little fancier if you want to preserve the case of the character. It also assumes, but does not verify shift is non-negative. That case may cause problems with the modulus operation you will need to guard against... or better yet prevent. Still, since this is tagged as homework, that's the sort of thing you should work through.
If you can assume ASCII, it is easier.
Characters are no more than simple numbers: only the interpretation of said numbers changes. In ASCII all letters are sequential; so the number for 'A' + 5 is the number for 'F'; 'F' - 1 is 'E' ..., ...
int ch = 'J';
ch -= 2; putchar(ch);
ch -= 3; putchar(ch);
ch += 7; putchar(ch); putchar(ch);
ch += 3; putchar(ch);
puts("");
Just pay attention to wrapping!
If you can't assume ASCII, you need to convert characters yourself. Something like:
char charr[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int ndx = 9; /* charr[9] is 'J' */
ndx -= 2; putchar(charr[ndx]);
ndx -= 3; putchar(charr[ndx]);
ndx += 7; putchar(charr[ndx]); putchar(charr[ndx]);
ndx += 3; putchar(charr[ndx]);
puts("");
Do not forget the wrapping
Other people have pointed out that you can use ASCII.
An easy way to handle wrapping is with modulus arithmetic:
char result, ch;
int offset;
... // Populate ch with the letter to be changed and offset with the number.
result = ch - 'a';
result = (result + offset) % 26; // 26 letters in the alphabet
result += 'a';
char shift_char(char c, char shift)
{
if(isalpha(c)) {
if (c>='A' && c<='Z') {
return 'A' + ( (c - 'A' + shift) % 26);
} else if(c>='a' && c<='z') {
return 'a' + ( (c - 'a' + shift) % 26);
}
}
return c;
}