print number of digits of an integer - c

How to print just for example 3 last digits of a signed integer as hexadecimal number?
int num = -5;
printf("%03X\n", num);
prints FFFFFFFB
I need it to print just FFB

How about:
void printLastThreeHex(int num)
{
size_t len = 0;
char tmp[sizeof(int)*2 + 1]; // two hex chars for each byte + null char
char* str = tmp;
sprintf(tmp, "%03x", num);
len = strlen(tmp);
if (len > 3)
{
str = tmp + len - 3;
}
printf("%s\n", str);
}

I believe the best and clearest way to do this would be a bitmask to hide everything except the last few digits. In your case, this would be printf("%X\n", num & 0xfff);:
0xfffffffb
0x00000fff
-----|||
0x00000ffb
You can adapt this to work with higher bits too. So if you want to extract 0xadb from 0xdeadbeef, it would look like this:
(0xdeadbeef & 0xfff000) >> 12 // gives 0xadb
(0xdeadbeef & 0xfff000) will mask out the bits you want, leaving 0xadb000. To remove these zeroes, right shift by 12 (each hex digit can represent 4 bits, and we need to remove 3 digits, so 4*3 = 12).

Related

Why does left-shifting an integer by 24-bit yield the wrong result?

I tried left-shifting a 32-bit integer by 24:
char *int_to_bin(int num) {
int i = 0;
static char bin[64];
while (num != 0) {
bin[i] = num % 2 + 48;
num /= 2;
i++;
}
bin[i] = '\0';
return (bin);
}
int main() {
int number = 255;
printf("number: %s\n", int_to_bin(number));
printf("shifted number: %s\n", int_to_bin(number << 24));
return 0;
}
OUTPUT:
number: 11111111
shifted number: 000000000000000000000000/
and i left-shift with 23-bit it yields this result:
0000000000000000000000011111111
Well Why is it like that and what's the matter with '/' at the end of the wrong result?
Two things:
If number has the value 255 then number << 24 has the numerical value 4278190080, which overflows a 32-bit signed integer whose largest possible value is 2147483647. Signed integer overflow is undefined behavior in C, so the result could be anything at all.
What probably happens in this case is that the result of the shift is negative. When num is negative then num % 2 may take the value -1, so you store character 47 in the string, which is /.
Bit shifting math is usually better to do with unsigned types, where overflow is well-defined (it wraps around and bits just shift off the left and vanish) and num % 2 can only be 0 or 1. (Or write num & 1 instead.)
Your int_to_bin routine puts the least-significant bits at the beginning of the string (on the left), so the result is backwards from the way people usually write numbers (with the least-significant bits on the right). You may want to rewrite it.
Shift works fine, you simply print it from the wrong direction.
char *int_to_bin(char *buff, int num)
{
unsigned mask = 1U << (CHAR_BIT * sizeof(num) - 1);
char *wrk = buff;
for(; mask; mask >>= 1)
{
*wrk++ = '0' + !!((unsigned)num & mask);
}
*wrk = 0;
return buff;
}
int main()
{
char buff[CHAR_BIT * sizeof(int) + 1];
int number = 255;
printf("number: %s\n", int_to_bin(buff, number));
printf("shifted number: %s\n", int_to_bin(buff, number << 24));
return 0;
}
Shifting signed integers left is OK, but the right shift is implementation-defined. Many systems use arithmetic shift right and the result is not the same as using the bitwise one:
https://godbolt.org/z/e7f3shxd4
you are storing numbers backwards
you are using signed int32 while shifting by 23 results needs more than 32 bits to handle that operation ...you should use long long int
signed integer can lead to wrong answers as 1<<31 is -1 which results in bad characters in string
finally using unsigned long long int with storing numbers in correct order will produce correct string
you should try re write code on your own before seeing this improved version of your code
#include<stdio.h>
#include<stdlib.h>
char *int_to_bin( unsigned long long int num) {
int i = 0;
static char bin[65];
while (i != 64) {
bin[63-i] = num % 2 + 48;
num /= 2;
i++;
}
bin[64] = '\0';
return (bin);
}
int main() {
unsigned long long int number = 255;
printf("number 1: %s\n", int_to_bin(number));
printf("number 2: %s\n", int_to_bin(number << 24));
return 0;
}

integer to string itox function in C

I got this from somewhere but i don't kind of understand the meaning behind it.
How does this actually works?
void itox(unsigned int i, char *s)
{
unsigned char n;
s += 4;
*s = '\0';
for (n = 4; n != 0; --n) {
*--s = "0123456789ABCDEF"[i & 0x0F];
i >>= 4;
}
}
Thank you.
It assumes that s is a buffer of length 5 (including the null terminator) and writes the hex representation of i there. The result is the hex representation of i modulo 65536 (for lots of old systems, unsigned int has a range of 0 to 65535).
s += 4;
*s = '\0';
This goes to the end of s and puts a null terminator there.
for (n = 4; n != 0; --n) {
Now we loop backwards through the result string and fill in the appropriate char.
"0123456789ABCDEF"[i & 0x0F];
This selects the correct char. i & 0x0F gets the least significant hex value and by using that as a subscript for array access on "0123456789ABCDEF", the respective char is obtained.
*--s = ...
The char that is obtained is put in the correct place and the pointer is decreased again so the next position can be filled in the next run through the loop.
i >>= 4;
We now shift the number by four bits, removing the four bits we just converted into a hex digit. Now the next four bits will be the least significant hex digit.
Example
let's take the number 58008. In hex it is 0xE298. Mod 16 it is 8, so "0123456789ABCDEF"[8]; gets "8".
Then we shift it four bits, resulting in 3625. Mod 16 that's 9, and we get the "9". After the next shift we get 226, which mod 16 is 2, and one shift later we get 14. "0123456789ABCDEF"[14] is "E".
Assemble those results backwards and you get E298.
The interesting part here is
*--s = "0123456789ABCDEF"[i & 0x0F];
Here "0123456789ABCDEF" is a string literal which is stored in the compiler memory.
We are accessing this literal as an array. So, "0123456789ABCDEF"[0] will be the character '0' and "0123456789ABCDEF"[1] will be '1'
With that information, we can easily analyse the entire code.
s += 4; //Increment pointer s by 4
*s = '\0'; // last value to be '\0' to end the string
for (n = 4; n != 0; --n) {
*--s = "0123456789ABCDEF"[i & 0x0F];
i >>= 4;
}
// say i is 0x231
// For n == 4, i & 0x0F will be 1,
// *--s will point s to the third element in the array, and
// this will be assigned to 1.
// i <<4 will be i/16, so i will be 0x23
// for n == 3,
// *--s will point to second element of array, which will be 3.
// and so on.
Finally what you get is the hexadecimal value of the integer in s

Decimal to BCD to ASCII

Perhaps this task is a bit more complicated than what I've written below, but the code that follows is my take on decimal to BCD. The task is to take in a decimal number, convert it to BCD and then to ASCII so that it can be displayed on a microcontroller. As far as I'm aware the code works sufficiently for the basic operation of converting to BCD however I'm stuck when it comes to converting this into ASCII. The overall output is ASCII so that an incremented value can be displayed on an LCD.
My code so far:
int dec2bin(int a){ //Decimal to binary function
int bin;
int i =1;
while (a!=0){
bin+=(a%2)*i;
i*=10;
a/=2;
}
return bin;
}
unsigned int ConverttoBCD(int val){
unsigned int unit = 0;
unsigned int ten = 0;
unsigned int hundred = 0;
hundred = (val/100);
ten = ((val-hundred*100)/10);
unit = (val-(hundred*100+ten*10));
uint8_t ret1 = dec2bin(unit);
uint8_t ret2 = dec2bin((ten)<<4);
uint8_t ret3 = dec2bin((hundred)<<8);
return(ret3+ret2+ret1);
}
The idea to convert to BCD for an ASCII representation of a number is actually the "correct one". Given BCD, you only need to add '0' to each digit for getting the corresponding ASCII value.
But your code has several problems. The most important one is that you try to stuff a value shifted left by 8 bits in an 8bit type. This can never work, those 8 bits will be zero, think about it! Then I absolutely do not understand what your dec2bin() function is supposed to do.
So I'll present you one possible correct solution to your problem. The key idea is to use a char for each individual BCD digit. Of course, a BCD digit only needs 4 bits and a char has at least 8 of them -- but you need char anyways for your ASCII representation and when your BCD digits are already in individual chars, all you have to do is indeed add '0' to each.
While at it: Converting to BCD by dividing and multiplying is a waste of resources. There's a nice algorithm called Double dabble for converting to BCD only using bit shifting and additions. I'm using it in the following example code:
#include <stdio.h>
#include <string.h>
// for determining the number of value bits in an integer type,
// see https://stackoverflow.com/a/4589384/2371524 for this nice trick:
#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \
+ (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3))
// number of bits in unsigned int:
#define UNSIGNEDINT_BITS IMAX_BITS((unsigned)-1)
// convert to ASCII using BCD, return the number of digits:
int toAscii(char *buf, int bufsize, unsigned val)
{
// sanity check, a buffer smaller than one digit is pointless
if (bufsize < 1) return -1;
// initialize output buffer to zero
// if you don't have memset, use a loop here
memset(buf, 0, bufsize);
int scanstart = bufsize - 1;
int i;
// mask for single bits in value, start at most significant bit
unsigned mask = 1U << (UNSIGNEDINT_BITS - 1);
while (mask)
{
// extract single bit
int bit = !!(val & mask);
for (i = scanstart; i < bufsize; ++i)
{
// this is the "double dabble" trick -- in each iteration,
// add 3 to each element that is greater than 4. This will
// generate the correct overflowing bits while shifting for
// BCD
if (buf[i] > 4) buf[i] += 3;
}
// if we have filled the output buffer from the right far enough,
// we have to scan one position earlier in the next iteration
if (buf[scanstart] > 7) --scanstart;
// check for overflow of our buffer:
if (scanstart < 0) return -1;
// now just shift the bits in the BCD digits:
for (i = scanstart; i < bufsize - 1; ++i)
{
buf[i] <<= 1;
buf[i] &= 0xf;
buf[i] |= (buf[i+1] > 7);
}
// shift in the new bit from our value:
buf[bufsize-1] <<= 1;
buf[bufsize-1] &= 0xf;
buf[bufsize-1] |= bit;
// next bit:
mask >>= 1;
}
// find first non-zero digit:
for (i = 0; i < bufsize - 1; ++i) if (buf[i]) break;
int digits = bufsize - i;
// eliminate leading zero digits
// (again, use a loop if you don't have memmove)
// (or, if you're converting to a fixed number of digits and *want*
// the leading zeros, just skip this step entirely, including the
// loop above)
memmove(buf, buf + i, digits);
// convert to ascii:
for (i = 0; i < digits; ++i) buf[i] += '0';
return digits;
}
int main(void)
{
// some simple test code:
char buf[10];
int digits = toAscii(buf, 10, 471142);
for (int i = 0; i < digits; ++i)
{
putchar(buf[i]);
}
puts("");
}
You won't need this IMAX_BITS() "magic macro" if you actually know your target platform and how many bits there are in the integer type you want to convert.

decimal to hexadecimal unit convertion

I have the next code that converts an decimal unit to an hexadecimal.If i introduce the number 4095 for example, it returns the FFF hexadecimal,but the problem is that i want the number to be printed in a 2 byte format like this(with zeros on the left):
4095 -> 0FFF,
33 -> 0021
i know that there are simpler ways to do this like:
int number = 4095
printf("%04x",number);
but i want to do the conversions and the 2 byte format by myself,but i dont know how to do the zero's on the left procedure.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char Hexadecimal(int rest);
int main()
{
char num_hex,*x,c[2],;
int number = 4095,d,rest;
x = calloc(12,sizeof(char));
for(d = number;d > 0;d/=16)
{
rest = d % 16;
num_hex = Hexadecimal(rest);
sprintf(c,"%c",num_hex);
strcat(x,c);
}
strrev(x);
printf("[%s]\n",x);
return 0;
}
char Hexadecimal(int rest)
{
char letter;
switch(rest)
{
case 10:
letter = 'A';
break;
case 11:
letter = 'B';
break;
case 12:
letter = 'C';
break;
case 13:
letter = 'D';
break;
case 14:
letter = 'E';
break;
case 15:
letter = 'F';
break;
default:
letter = '0' + rest;
}
return letter;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char const Hexadecimal[] = "0123456789ABCDEF";
int main()
{
char num_hex,*x,c[2],;
int number = 4095,d,rest;
x = calloc(12,sizeof(char));
for(d = number;d > 0;d/=16)
{
rest = d % 16;
num_hex = Hexadecimal[rest];
sprintf(c,"%c",num_hex);
strcat(x,c);
}
strrev(x);
printf("[%s]\n",x);
return 0;
}
I think it's easier to think of these problems in terms of binary arithmetic.
Here's an implementation that uses binary math and no standard library functions. It's written in a general way, so you could replace short with int or long and get a similarly correct result (as long as you replace the short type everywhere in the function with your desired type).
char* shortToHex(short value)
{
static char tmp[(sizeof(short)*2)+3] = "0x";
const char* hex = "0123456789ABCDEF";
int i = sizeof(short);
while(i > 0)
{
i--;
char most_significant_nibble = (value >> (i * 8 + 4)) & 0xF;
char least_significant_nibble = (value >> (i * 8)) & 0xF;
tmp[2 + (sizeof(short)-i-1) * 2] = hex[most_significant_nibble];
tmp[2 + (sizeof(short)-i-1) * 2 + 1] = hex[least_significant_nibble];
}
tmp[(sizeof(short)*2)+2] = 0;
return tmp;
}
Let me explain line-by-line so you understand.
static char tmp[(sizeof(short)*2)+3] = "0x";
We need a buffer large enough to hold the amount of nibbles in a short (16 bits == 2 bytes == 4 characters, so two per byte in general) plus a '\0' terminator, plus the "0x" string.
const char* hex = "0123456789ABCDEF";
We need to be able to grab a hexadecimal character based on an array index.
while(i > 0)
{
i--;
...
}
We need a loop counter that counts down from the number of bytes in a short. We're going to loop starting at the most significant byte, to build up the string left-to-right.
char most_significant_nibble = (value >> (i * 8 + 4)) & 0xF;
char least_significant_nibble = (value >> (i * 8)) & 0xF;
tmp[2 + (sizeof(short)-i-1) * 2] = hex[most_significant_nibble];
tmp[2 + (sizeof(short)-i-1) * 2 + 1] = hex[least_significant_nibble];
We're going to shift the bits in the input value to the right, so that the desired bits are in the least-significant nibble of a temporary character. We determine how many bits to shift by multiplying the index (in the case of a 16-bit short, i would start out being 1 since we decremented it, so we'll have 1 * 8 + 4 the first time through the loop, and shift 12 bits to the right, leaving us with the most significant nibble in the short. The second line would shift 8 bits to the right and grab the second nibble. (and so on) The & 0xF portion ensures that whatever 4-bit value we have shifted furthest to the right is the only value in the temporary char.
Then we take those temporary values and set them into the tmp array at the appropriate place. (the math is a little tricky since our index as at the most significant - highest - byte of the value, but we want to place the character at the lowest point in the tmp value.)
tmp[(sizeof(short)*2)+2] = 0;
return tmp;
Lastly, we terminate the tmp string with a '\0' and return it.

unsigned to hex digit

I got a problem that says: Form a character array based on an unsigned int. Array will represent that int in hexadecimal notation. Do this using bitwise operators.
So, my ideas is the following: I create a mask that has 1's for its 4 lowest value bits.
I push the bits of the given int by 4 to the right and use & on that int and mask. I repeat until (int != 0). My question is: when I get individual hex digits (packs of 4 bits), how do I convert them to a char? For example, I get:
x & mask = 1101(2) = 13(10) = D(16)
Is there a function to convert an int to hex representation, or do I have to use brute force with switch statement or whatever else?
I almost forgot, I am doing this in C :)
Here is what I mean:
#include <stdio.h>
#include <stdlib.h>
#define BLOCK 4
int main() {
unsigned int x, y, i, mask;
char a[4];
printf("Enter a positive number: ");
scanf("%u", &x);
for (i = sizeof(usnsigned int), mask = ~(~0 << 4); x; i--, x >>= BLOCK) {
y = x & mask;
a[i] = FICTIVE_NUM_TO_HEX_DIGIT(y);
}
print_array(a);
return EXIT_SUCCESS;
}
You are almost there. The simplest method to convert an integer in the range from 0 to 15 to a hexadecimal digit is to use a lookup table,
char hex_digits[] = "0123456789ABCDEF";
and index into that,
a[i] = hex_digits[y];
in your code.
Remarks:
char a[4];
is probably too small. One hexadecimal digit corresponds to four bits, so with CHAR_BIT == 8, you need up to 2*sizeof(unsigned) chars to represent the number, generally, (CHAR_BIT * sizeof(unsigned int) + 3) / 4. Depending on what print_array does, you may need to 0-terminate a.
for (i = sizeof(usnsigned int), mask = ~(~0 << 4); x; i--, x >>= BLOCK)
initialising i to sizeof(unsigned int) skips the most significant bits, i should be initialised to the last valid index into a (except for possibly the 0-terminator, then the penultimate valid index).
The mask can more simply be defined as mask = 0xF, that has the added benefit of not invoking undefined behaviour, which
mask = ~(~0 << 4)
probably does. 0 is an int, and thus ~0 is one too. On two's complement machines (that is almost everything nowadays), the value is -1, and shifting negative integers left is undefined behaviour.
char buffer[10] = {0};
int h = 17;
sprintf(buffer, "%02X", h);
Try something like this:
char hex_digits[] = "0123456789ABCDEF";
for (i = 0; i < ((sizeof(unsigned int) * CHAR_BIT + 3) / 4); i++) {
digit = (x >> (sizeof(unsigned int) * CHAR_BIT - 4)) & 0x0F;
x = x << 4;
a[i] = hex_digits[digit];
}
Ok, this is where I got:
#include <stdio.h>
#include <stdlib.h>
#define BLOCK 4
void printArray(char*, int);
int main() {
unsigned int x, mask;
int size = sizeof(unsigned int) * 2, i;
char a[size], hexDigits[] = "0123456789ABCDEF";
for (i = 0; i < size; i++)
a[i] = 0;
printf("Enter a positive number: ");
scanf("%u", &x);
for (i = size - 1, mask = ~(~0 << 4); x; i--, x >>= BLOCK) {
a[i] = hexDigits[x & mask];
}
printArray(a, size);
return EXIT_SUCCESS;
}
void printArray(char a[], int n) {
int i;
for (i = 0; i < n; i++)
printf("%c", a[i]);
putchar('\n');
}
I have compiled, it runs and it does the job correctly. I don't know... Should I be worried that this problem was a bit hard for me? At faculty, during exams, we must write our code by hand, on a piece of paper... I don't imagine I would have done this right.
Is there a better (less complicated) way to do this problem? Thank you all for help :)
I would consider the impact of potential padding bits when shifting, as shifting by anything equal to or greater than the number of value bits that exist in an integer type is undefined behaviour.
Perhaps you could terminate the string first using: array[--size] = '\0';, write the smallest nibble (hex digit) using array[--size] = "0123456789ABCDEF"[value & 0x0f], move onto the next nibble using: value >>= 4, and repeat while value > 0. When you're done, return array + size or &array[size] so that the caller knows where the hex sequence begins.

Resources