Interpreting a hex number as decimal - c

I have an embedded system running C-code which works pretty straightforwardly; it reads characters via a serial connection, interprets the received chars as hexadecimal numbers and depending on what was received, proceeds to do something else. However, there is one very special case where the chars received are decimal instead of hex. Since this case is very rare (kind of an error case of an error case), I don't wish to modify the actual character reception to decide whether to interpret the received value as dec or hex, but rather to add a quick algorithm into the case handling where I change the number into decimal.
What would you say is the fastest (as in most efficient processor-wise) way of doing this? Since the software is running on a small MCU, any C library functions are not an option since I don't wish to add any more unnecessary #include's, so a purely mathematical algorithm is what I'm searching for.
Just to be clear, I'm not asking the quickest way to do a basic hex-to-dec- conversion as in 0x45 -> dec 69, but what I want to do is to transform eg. 0x120 into decimal 120.
Thanks in advance!
EDIT: Sorry, I'll try to explain in more detail. The actual code is way too long, and I think pasting it here is unnecessary. So here's what happens:
First I read a received number from the serial line, let's say "25". Then I turn it into hex number, so I have a variable with the read value, let's say X = 0x25. This works already fine, and I don't want to do modifications to this. What I would like to do now in this very special case is just to change the interpretation of the variable so that instead of X == 0x25, X==25. Hexadecimal 0x25 turns into decimal 25. There has to be some kind of mathematical formula for such a change, without the need of any processor-specific instructions or library functions?

If I'm understanding correctly, you've already converted a stream of ASCII characters into a char/int variable, assuming them to be a stream of hex-digits. In some cases, they were actually a stream of decimal digits (e.g. you received 45 and, treating this as hex, got a variable with value 69 when -- in one special case -- you actuially want its value to be 45.
Assuming two-characters, (00-ff in general, but for "was meant to be decimal" we're talking 00-99) then:
int hexVal = GetHexStringFromSerialPort() ;
int decVal = 10*(hexVal >> 4) + (hexVal & 0x0f) ;
should do the trick. If you've got longer strings, you'll need to extend the concept further.

Just do a simple while loop like this, supposing onum and dnum are unsigned integers
dnum = 0;
while (onum) {
digit = onum & 0xF;
dnum = dnum*10 + digit;
onum >>= 4;
}
this supposes that onum is really of the form that you describe (no hexdigits that are >9). It just succs the least significant hexdigit out of your number and adds it to your decimal.

Checking if your string starts with 0x characters and removing them should do the trick.

Related

How does atof.c work? Subtracting an ASCII zero from an ASCII digit makes it an int? Am I missing something?

So as part of my C classes, for our first homework we are supposed to implement our own atof.c function, and then use it for some tasks. So, being the smart stay-at-home student I am I decided to look at the atof.c source code and adapt it to meet my needs. I think i'm on board with most of the operations that this function does, like counting the digits before and after the decimal point, however there is one line of code that I do not understand. I'm assuming this is the line that actually converts the ASCII digit into a digit of type int. Posting it here:
frac1 = 10*frac1 + (c - '0');
in the source code, c is the digit that they are processing, and frac1 is an int that stores some of the digits from the incoming ASCII string. but why does c- '0' work?? And as a followup, is there another way of achieving the same result?
There is no such thing as "text" in C. Just APIs that happen to treat integer values as text information. char is an integer type, and you can do math with it. Character literals are actually ints in C (in C++ they're char, but they're still usable as numeric values even there).
'0' is a nice way for humans to write "the ordinal value of the character for zero"; in ASCII, that's the number 48. Since the digits appear in order from 0 to 9 in all encodings I'm aware of, you can convert from the ordinal value in the encoding (e.g. ASCII) to actual numeric values by subtracting away '0' to get actual int values from 0 to 9.
You could just as easily subtract 48 directly (when compiled, it would be impossible to tell which option you used; 48 and ASCII '0' are indistinguishable), it would just be less obvious what you were doing to other people reading your source code.
The ASCII value of '0' is the 48'th character in code page 437 (IBM default character set). Similarly, '1' is the 49'th etc. Subtracting '0' instead of a magic number such as 48 is much clearer as far as self-documentation goes.

How to check whether a hexadecimal number(%x) starts with a certain number or not in C?

I'm working on an operating system related project now and there is one step which requires to check whether a hexadecimal number starts with 3 or not, using C.
Currently my idea is to convert that hexadecimal number into a string and check the initial character but just cannot find out any documentation for doing that. Anybody has any idea? Maybe just a hint.
There are lots of methods to convert a number to a hex string (sprintf comes to mind; How can I convert an integer to a hexadecimal string in C? list a few more) – but, why should you?
A full hex number is formed by converting each nibble (4 bits) to a hexadecimal 'digit'. To get just the first, you can divide your value by 16 until you have reached the final (= 'first', in the left-to-right notation common for both decimal and hexadecimal values) digit. If that's a 3 you are done.
Assuming value is an unsigned number:
while (value > 15)
value >>= 4;
and then check if value == 3.
Your idea is not bad, you can use sprintf, it functions like printf/fprintf but instead of printing
on screen (or to be more precise: writing into a FILE* buffer), it stores the contents in a char buffer.
char value[16]; // more than enough space for 4 byte values
int reg = 0x3eef;
sprintf(value, "%x", reg);
if(value[0] == '3')
printf("The hexadecimal number 0x%s starts with a 3.\n", value);

Purpose of using octal for ASCII

Why would a C programmer use escape sequences (oct/hex) for ASCII values rather than decimal?
Follow up: does this have to do with either performance or portability?
Example:
char c = '\075';
You use octal or hexadecimal because there isn't a way to specify decimal codes inside a character literal or string literal. Octal was prevalent in PDP-11 code. These days, it probably makes more sense to use hexadecimal, though '\0' is more compact than '\x0' (so use '\0' when you null terminate a string, etc.).
Also, beware that "\x0ABad choice" doesn't have the meaning you might expect, whereas "\012007 wins" probably does. (The difference is that a hex escape runs on until it comes across a non-hex digit, whereas octal escapes stop after 3 digits at most. To get the expected result, you'd need "\x0A" "Bad choice" using 'adjacent string literal concatenation'.)
And this has nothing to do with performance and very little to do with portability. Writing '\x41' or '\101' instead of 'A' is a way of decreasing the portability and readability of your code. You should only consider using escape sequences when there isn't a better way to represent the character.
No it does not have anything to do with performance and portability. It is just one convenient way to define character literals and to use in string literal specially for non-printable characters.
It has nothing to do with performance nor portability. In fact, you don't need any codes at all, instead of this:
char c = 65;
You can simply write:
char c = 'A';
But some characters are not so easy to type, e.g. ASCII SOH, so you might write:
char c = 1; // SOH
Or any other form, hexadecimal, octal, depending on your preference.
It has nothing to do with performance nor with portability. It is simply that the ASCII character set (as are its derivatives up to UTF) is organized in bytes and bits. For example, the 32 first characters are the control characters, 32 = 040 = 0x20, ASCII code of 'A' is 65 = 0101 = 0x41 and 'a' is 97 = 0141 = 0x61, ASCII code of '0' is 48 = 060 = 0x30.
I do not know for you, but for me '0x30' and 0x'41' are easier to remember and use in manual operations than 48 and 65.
By the way a byte represents exactly all value between 0 and 255 that is 0 and 0xFF ...
I didn't know this works.
But I got immediatly a pretty usefull idea for it.
Imagin you have got a low memory enviroment and have to use a permission system like the unix folder permissions.
Lets say there are 3 groups and for each group 2 different options which can be allowed or denied.
0 means none of both options,
1 means first option allowed,
2 means second option allowed and
3 means both allowed.
To store the permissions you could do it like:
char* bar = "213"; // first group has allowed second option, second group has first option allowed and third has full acces.
But there you have four byte storage for that information.
Ofc you could just convert that to decimal notation. But thats less readable.
But now as I know this....
doing:
char bar = '\0213';
Is pretty readable and also saving memory!
I love it :D

How does conversion specifier work?

Can I check my understanding of scanf?
int a;
scanf("%d",&a);
If I input 13, does conversion specifier convert 13 to binary and stored it in a?
If input is 13.3, then does it convert decimal fraction 13.3 to binary and store it in a?
I don't know about your knowledge in C, but how data stores in C is really is very important and interesting topic.
whenever data stores in variable it is always in binary format, it is not possible to see that binary data in simple C language but you can see the binary in embedded C. Where we pass data in any port or any program providing data internally and then sending the output to any port which is connected to the led.
Suppose the if you passed -1 in a port and the port is sending output to led connected with it then you can see all leds connected with it will glow.
In C you can observe like that
char c=-1;
printf("%u",c);
output=255
%u is used for unsigned integer it is not an error you can check it.
int and char always store in same fashion means if you try store char A then I think so it's ASCII value 65 will stored in binary way.
char ch='A';
printf("%d",ch);
output 65
same
int i=65;
printf("%c",i);
output A;
But float is stored completely different from int it has three different parts one is sign bit, second is mantissa and then exponent.
suppose
float f=5.55;
if(f==5.55)
pf("true");
else
pf("false");
output : false
now again
float f=5.25;
if(f==5.25)
pf("true");
else
pf("false");
output : true
so it's all about data stores in memory very important topic generally teacher not discuss in their classes.
Hope use full for you.
OP: If I input 13, does conversion specifier convert 13 to binary and stored it in a?
A: Yes. When user enters 1, 3, Enter, scanf("%d", ...) receives optional leading white-space (which there is none) then the chars '1', '3', and '\n'. Seeing '\n' is not a digit, it is put back into stdin for the next IO operation. The '1', '3' are converted to an int with the value of 13 **. The 13 is stored at &a. scanf() then returns a value of 1 which the code unfortunately does not use.
OP: If input is 13.3, then does it convert decimal fraction 13.3 to binary and store it in a?
A: No. scanf("%d", ...) reads the text as described above but instead of stopping at '\n', it stops at '.' and puts that back for subsequent IO. The '1', '3' is converted to 13 and stored in &a. The next IO operation will start with '.'. and likely cause trouble.
Suggest instead:
char buf[40];
fgets(buf, sizeof buf, stdin);
if (sscanf(buf, "%d", &a) != 1) Handle_IntNotEntered(buf);
Other approaches exist , like using strtol(), but the important issue is that users enter all sorts of unexpected text and the input needs validation and error handling.
** The 13 is typically stored in binary - I know of no machine that that does otherwise. But the details of how the 13 is stored should not make a difference. a will have the value of 13, if it is stored in binary, BCD or with 13 chipmunks running on 13 wheels.
In short, yes. There are different encoding by which computer can store data. Some machine are little endian some are big endian.
For, floating point number you can refer,
IEEE floating point
In short no. But you can change the code to almost work.
float a;
scanf("%f",&a);
However .3 can never be stored exactly in a float.
There is also a decimal type, a decimal based float (as opposed to the binary based one). It can store 0.3 exactly (and all other numbers you can type, up to its precision). But you can still not store 1/3 (you would need a fraction store to store that precisely). Or π ( you need a symbol store to store that). However you don't always need to be precise.

atoi() in C saving zip codes

Using atoi() while reading from a file and it is dropping the first 0 in some of the zip codes, for example:
int x = atoi("06461");
seems to be saving x = 6461. Is dropping non significant 0's part of the atoi function?
It doesn't drop zero. It stores the number. And as a number (decimal) 06461 and 6461 is exactly the same value. It's up to you how to present the number — with (printf("%05d",zip)) or without (%d in case of printf) leading zero.
P.S. Note, that c folks are mightily confused by leading zeros, they tend to see numbers as octal then.
P.P.S. And I fully support Joachim's comment to your question.

Resources