0

I have a 14 bits address with leading zeros and I want to divide it into page number and offset. I was trying to solve it with shifting the address but my offset is getting wrong value, however the page number shows up correctly. Can you point where I am wrong here?

Page numbers bits are [2:5] Offset bits are [6:15]

struct Mem
{
 unsigned int address = 0x0000;
 unsigned int pageNum = 0x0;
 unsigned int pageOff = 0x000;
}

int main()
{
 Mem box;

 box.address = 0x0ad0;
 box.pageNum = (box.address << 2) >> 12;
 box.pageOff = (box.address << 6) >> 6;

return 0;
}
Qaimaq
  • 117
  • 7
  • You are leaving out information. How is `box.number` even possible if it's not a member of `Mem`? Please provide a reproducible example that produces the wrong answer when run, and then tell us what you expected the answer to be. – Gillespie Apr 01 '21 at 20:11
  • `(address << 2) >> 12` is the same as `address >> 10`, which is not what you want. Bits are typically counted from right-to-left, and `0x0ad0` is bits `0000101011010000` (technically, `00000000000000000000101011010000` since an `unsigned int` is 32 bits on most modern systems) so you are saying that you want bits `0100` (4) for the page number, and bits `0000101011` (43) for the offset? – Remy Lebeau Apr 01 '21 at 20:14
  • @Gillespie sorry, my bad. Changed it – Qaimaq Apr 01 '21 at 20:16
  • Have you considered using bitfields instead of manual shifts? – Remy Lebeau Apr 01 '21 at 20:16
  • @RemyLebeau yes, that is exactly what I want – Qaimaq Apr 01 '21 at 20:17
  • @RemyLebeau I do not know what is it, could you provide some links so I can read about it – Qaimaq Apr 01 '21 at 20:18
  • @RemyLebeau: "(address << 2) >> 12 is the same as address >> 10" No it's not. The first truncates the top two bits (due to being unsigned), and the second one doesn't. – Mooing Duck Apr 01 '21 at 20:26
  • 1
    @MooingDuck But in this case, the top 2 bits aren't being used to begin with, so the two statements are *effectively* the same. In any case, it would make more sense to only shift-right and mask off the unwanted bits with `&`. – Remy Lebeau Apr 01 '21 at 20:27
  • @RemyLebeau Bitfields do not have a standard representation so they are not a portable way of reading out bits from e.g. an `unsigned int`. – nielsen Apr 01 '21 at 20:53

1 Answers1

3

Shifting left to clear out digits is a dangerous game, in my opinion, because if you happen to be using int instead of unsigned int, you might get a sign extend you didn't intend. I recommend shifting and masking:

If your address looks like this:

X X P P P P O O O O O O O O O O

Where P is page numbers and O is offsets then you would do:

box.pageNum = (box.address >> 10) & 0xf; // 0xf is 1111 in binary, so it only keeps the right 4 binary digits
box.pageOff = box.address & 0x3ff; // 0x3ff is 1111111111 in binary, so it only keeps the right 10 digits

However, as Remy pointed out you could just use bit fields (https://en.cppreference.com/w/c/language/bit_field) like:

struct Address
{
    unsigned int offset : 10;
    unsigned int page_num : 4;
}

...

Address a;
a.page_num; // assign it directly
a.offset; // assign it directly
Gillespie
  • 5,780
  • 3
  • 32
  • 54
  • Thank you, I will try both. – Qaimaq Apr 01 '21 at 20:25
  • How can I give an hex value to Address struct in your example? Can I do something like this: `Address ttt = {0x0ad0}`? – Qaimaq Apr 01 '21 at 20:33
  • Hmm, actually bit fields might not have been as portable a solution as I thought. Seems like you would have to combine with union for assignment: https://stackoverflow.com/a/37712054/2516916 – Gillespie Apr 01 '21 at 20:38
  • Note that the top answer on that page is to assign with shift and mask. Shift and mask is probably the most portable way to break a number into bit fields because hacky methods mean you have to worry about padding or endianness. – Gillespie Apr 01 '21 at 20:41