-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy path1.2_reverse_bits.c
executable file
·139 lines (127 loc) · 4.38 KB
/
1.2_reverse_bits.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**********************************************************************
** 1.2 : Reversing bits on an octet (octet means 8 bits or 1 byte)
**********************************************************************/
/*
** This a vanilla question for interviews :
** Write a function that takes a byte, reverses it, bit by bit (like the
** example) and returns the result.
**
** Your function must be declared as follows:
**
** unsigned char reverse_bits(unsigned char octet);
**
** Example:
**
** 1 byte
** _____________
** 0100 0001
** ||
** \/
** 1000 0010
*/
#include "bitwise.h"
/**********************************************************************
** 1st Method, for mortals like us ;)
***********************************************************************
** 1) As a byte is equal to 8 it means we will do it while (byte_len--)
**
** 2) We first check if b as a bit on the extreme right with (b & 1);
** if so we light bit 1 on r and move it juste 1 bit on the left by
** multiplying r by 2 with (r << 1)
**
** For novices: r = (r << 1) | (b & 1) might be easier to understand as:
**
** if (b % 2) | (b & 1)
** r = r + 1
** r *= 2; (r << 1)
**
** 3) Then we divide our unsigned char b by 2 to erase
** the bit located on the extreme right of the variable b.
** For reminder b >>= 1; is equivalent to b /= 2;
*/
unsigned char reverse_bits(unsigned char b)
{
unsigned char r = 0;
unsigned byte_len = 8;
while (byte_len--)
{
r = (r << 1) | (b & 1);
b >>= 1;
}
return (r);
}
/**********************************************************************
** 2nd Method, swap bits 4 by 4, 2 by 2 and then 1 by 1
***********************************************************************
**
** 1) The first formula swaps 4 by 4 by applying masks:
** (b & 0xF0) >> 4 applies a mask to the higher 4 bits
** (0x0F = 1111 0000) then move them to the right.
** (b & 0x0F) << 4 applies a mask to the lowest 4 bits
** (0x0F = 0000 1111) then move them to the left.
**
** 2) Now swapthem 2 by 2: 0xCC = 1100 1100 and 0x33 = 0011 0011
**
** 3) Finally swap 1 by 1: 0xAA = 1010 1010 and 0x55 = 0101 0101
**
** if you had byte b equal to 0111 0100 ('t') it will gradually becomes:
** 1) 0100 0111 ('G')
** 2) 00 01 11 01 (character 29, group separator, not a printable char)
** 3) 0 0 1 0 1 1 1 0 = 0010 1110 ('.')
*/
unsigned char reverse_bits2(unsigned char b)
{
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; // 1)
b = (b & 0xCC) >> 2 | (b & 0x33) << 2; // 2)
b = (b & 0xAA) >> 1 | (b & 0x55) << 1; // 3)
return (b);
}
/**********************************************************************
** 3rd Method, attributed to Rich Schroeppel in the Programming Hacks
** section http://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html
***********************************************************************
**
** 1) The multiply operation (b * 0x0202020202ULL) creates five separate
** copies of the 8-bit byte pattern to fan-out into a 64-bit value.
**
** 2) The AND operation (& 0x010884422010ULL) selects the bits that are in the
** correct (reversed) positions, relative to each 10-bit groups of bits.
**
** 3) Together the multiply and the AND operations copy the bits from the original
** byte so they each appear in only one of the 10-bit sets.
** The reversed positions of the bits from the original byte coincide with their
** relative positions within any 10-bit set.
**
** 4) The last step (% 0x3ff), which involves modulus division by 2^10 - 1
** has the effect of merging together each set of 10 bits
** (from positions 0-9, 10-19, 20-29, ...) in the 64-bit value.
** They do not overlap, so the addition steps underlying the modulus
** division behave like OR operations.
*/
unsigned char reverse_bits3(unsigned char b)
{
b = (b * 0x0202020202ULL & 0x010884422010ULL) % 0x3ff;
return (b);
}
/*
** Test program : . (equal to 046 in ASCII table, 00101110 in binary)
** becomes t (116 in ASCII table, 01110100 in binary).
** byte 00101110 ('.') is indeed the reverse of byte 01110100 ('t').
*/
int main(void)
{
unsigned char c;
c = '.';
write(1, &c, 1);
write(1, "\n", 1);
c = reverse_bits(c);
write(1, &c, 1);
write(1, "\n", 1);
c = reverse_bits2(c);
write(1, &c, 1);
write(1, "\n", 1);
c = reverse_bits3(c);
write(1, &c, 1);
write(1, "\n", 1);
return (0);
}