9.1 Bitwise Operations: Binary Operation Applications
9.1 Bitwise Operations: Binary Operation Applications
9.1 Bitwise Operations: Binary Operation Applications
if(((iVal/2)*2) == iVal)
// This code is executed for even values
else
// This code is executed for odd values
165
166 Computer Organization and Design Fundamentals
3510 = 001000112 12410 = 011111002
9310 = 010111012 3010 = 000111102
Value 1 Value 2
Result
Value 1 0 1 1 0 1 0 1 1
Value 2 1 1 0 1 1 0 1 0
Resulting AND 0 1 0 0 1 0 1 0
Remember that the output of an AND is one if and only if all of the
inputs are one. In Figure 9-2, we see that ones only appear in the result
in columns where both of the original values equal one. In a C program,
the bitwise AND is identified with the operator '&'. The example in
Figure 9-2 can then be represented in C with the following code.
Chapter 9: Binary Operation Applications 167
int iVal1 = 0b01101011;
int iVal2 = 0b11011010;
int result = iVal1 & iVal2;
3510 (odd) 0 0 1 0 0 0 1 1
Odd/Even Mask 0 0 0 0 0 0 0 1
Bitwise AND Result 0 0 0 0 0 0 0 1
9310 (odd) 0 1 0 1 1 1 0 1
Odd/Even Mask 0 0 0 0 0 0 0 1
Bitwise AND Result 0 0 0 0 0 0 0 1
3010 (even) 0 0 0 1 1 1 1 0
Odd/Even Mask 0 0 0 0 0 0 0 1
Bitwise AND Result 0 0 0 0 0 0 0 0
if(!(iVal&0b00000001))
// This code is executed for even values
else
// This code is executed for odd values
The bitwise AND can also be used to clear specific bits. For
example, assume we want to separate the nibbles of a byte into two
different variables. The following process can be used to do this:
• Copy the original value to the variable meant to store the lower
nibble, then clear all but the lower four bits
• Copy the original value to the variable meant to store the upper
nibble, then shift the value four bits to the right. (See Section 3.7,
"Multiplication and Division by Powers of Two," to see how to
shift right using C.) Lastly, clear all but the lower four bits.
Example
Using bitwise operations, write a function in C that determines if an
IPv4 address is a member of the subnet 192.168.12.0 with a subnet
mask 255.255.252.0. Return a true if the IP address is a member and
false otherwise.
Solution
An IPv4 address consists of four bytes or octets separated from one
another with periods or "dots". When converted to binary, an IPv4
address becomes a 32 bit number.
The address is divided into two parts: a subnet id and a host id. All
of the computers that are connected to the same subnet, e.g., a company
or a school network, have the same subnet id. Each computer on a
subnet, however, has a unique host id. The host id allows the computer
to be uniquely identified among all of the computers on the subnet.
The subnet mask identifies the bits that represent the subnet id.
When we convert the subnet mask in this example, 255.255.252.0, to
binary, we get 11111111.11111111.11111100.00000000.
The bits that identify the subnet id of an IP address correspond to the
positions with ones in the subnet mask. The positions with zeros in the
subnet mask identify the host id. In this example, the first 22 bits of any
IPv4 address that is a member of this subnet should be the same,
170 Computer Organization and Design Fundamentals
specifically they should equal the address 192.168.12.0 or in binary
11000000.10101000.00001100.00000000.
So how can we determine if an IPv4 address is a member of this
subnet? If we could clear the bits of the host id, then the remaining bits
should equal 192.168.12.0. This sounds like the bitwise AND. If we
perform a bitwise AND on an IPv4 address of this subnet using the
subnet mask 255.255.252.0, then the result must be 192.168.12.0
because the host id will be cleared. Let's do this by hand for one
address inside the subnet, 192.168.15.23, and one address outside the
subnet, 192.168.31.23. First, convert these two addresses to binary.
192.168.15.23 = 11000000.10101000.00001111.00010111
192.168.31.23 = 11000000.10101000.00011111.00010111
IP Address 11000000.10101000.00001111.00010111
Subnet mask 11111111.11111111.11111100.00000000
Bitwise AND 11000000.10101000.00001100.00000000
IP Address 11000000.10101000.00011111.00010111
Subnet mask 11111111.11111111.11111100.00000000
Bitwise AND 11000000.10101000.00011100.00000000
Notice that the result of the first bitwise AND produces the correct
subnet address while the second bitwise AND does not. Therefore, the
first address is a member of the subnet while the second is not.
The code to do this is shown below. It assumes that the type int is
defined to be at least four bytes long. The left shift operator '<<' used in
the initialization of sbnt_ID and sbnt_mask pushes each octet of
the IP address or subnet mask to the correct position.
Original value 1 0 0 1 0 1 1 0
Mask 0 0 1 0 1 0 1 0
Bitwise OR 1 0 1 1 1 1 1 0
Example
Assume that a control byte is used to control eight sets of lights in
an auditorium. Each bit controls a set of lights as follows:
For example, if the house lighting, exit lighting, and stage lighting
are all on, the value of the control byte should be 100101002. What
mask would be used with the bitwise OR to turn on the aisle lighting
and the emergency lighting?
Solution
The bitwise OR uses a mask where a one is in each position that
needs to be turned on and zeros are placed in the positions meant to be
left alone. To turn on the aisle lighting and emergency lighting, bits 5
and 3 must be turned on while the remaining bits are to be left alone.
This gives us a mask of 001010002.
A B X
0 0 0
0 1 1
1 0 1
1 1 0
If we cover up the bottom two rows of this truth table leaving only
the rows where A=0 visible, we see that the value of B is passed along
to X, i.e., if A=0, then X equals B. If we cover up the rows where A=0
leaving only the rows where A=1 visible, it looks like the inverse of B
is passed to X, i.e., if A=1, then X equals the inverse of B. This
discussion makes a two-input XOR gate look like a programmable
inverter. If A is zero, B is passed through to the output untouched. If A
is one, B is inverted at the output.
Therefore, if we perform a bitwise XOR, the bit positions in the
mask with zeros will pass the original value through and bit positions in
the mask with ones will invert the original value. The example below
uses the mask 001011102 to toggle bits 1, 2, 3, and 5 of a binary value
while leaving the others untouched.
Original value 1 0 0 1 0 1 1 0
Mask 0 0 1 0 1 1 1 0
Bitwise XOR 1 0 1 1 1 0 0 0
Example
Assume a byte is used to control the warning and indicator lights on
an automotive dashboard. The following is a list of the bit positions and
the dashboard lights they control.
Determine the mask to be used with a bitwise XOR that when used
once a second will cause the left and right turn indicators to flash when
the emergency flashers are on.
Chapter 9: Binary Operation Applications 173
Solution
The bitwise XOR uses a mask with ones is in the positions to be
toggled and zeros in the positions to be left alone. To toggle bits 3 and
2 on and off, the mask should have ones only in those positions.
Therefore, the mask to be used with the bitwise XOR is 000011002.
detector 1
detector 2
Difference indicates
an error occurred
Signal A
Equals 1 when A≠B
Signal B
9.3 Parity
One of the most primitive forms of error detection is to add a single
bit called a parity bit to each piece of data to indicate whether the data
has an odd or even number of ones. It is considered a poor method of
error detection as it sometimes doesn't detect multiple errors. When
combined with other methods of error detection, however, it can
improve their overall performance.
There are two primary types of parity: odd and even. Even parity
means that the sum of the ones in the data element and the parity bit is
an even number. With odd parity, the sum of ones in the data element
and the parity bit is an odd number. When designing a digital system
that uses parity, the designers decide in advance which type of parity
they will be using.
Assume that a system uses even parity. If an error has occurred and
one of the bits in either the data element or the parity bit has been
inverted, then counting the number of ones results in an odd number.
From the information available, the digital system cannot determine
which bit was inverted or even if only one bit was inverted. It can only
tell that an error has occurred.
One of the primary problems with parity is that if two bits are
inverted, the parity bit appears to be correct, i.e., it indicates that the
data is error free. Parity can only detect an odd number of bit errors.
Some systems use a parity bit with each piece of data in memory. If
a parity error occurs, the computer will generate a non-maskable
interrupt, a condition where the operating system immediately
discontinues the execution of the questionable application.
Chapter 9: Binary Operation Applications 175
Example
Assume the table below represents bytes stored in memory along
with an associated parity bit. Which of the stored values are in error?
Data Parity
1 0 0 1 0 1 1 0 0
0 0 1 1 1 0 1 0 1
1 0 1 1 0 1 0 1 1
0 1 0 1 1 0 0 1 0
1 1 0 0 0 1 0 1 1
Solution
To determine which data/parity combinations have an error, count
the number of ones in each row. The rows with an odd sum have errors
while the rows with an even sum are assumed to contain valid data.
Data Parity
1 0 0 1 0 1 1 0 0 4 ones – even Æ no error
0 0 1 1 1 0 1 0 1 5 ones – odd Æ Error!
1 0 1 1 0 1 0 1 1 6 ones – even Æ no error
0 1 0 1 1 0 0 1 0 4 ones – even Æ no error
1 1 0 0 0 1 0 1 1 5 ones – odd Æ Error!
9.4 Checksum
For digital systems that store or transfer multiple pieces of data in
blocks, an additional data element is typically added to each block to
provide error detection for the block. This method of error detection is
common, especially for the transmission of data across networks.
One of the simplest implementations of this error detection scheme
is the checksum. As a device transmits data, it takes the sum of all of
the data elements it is transmitting to create an aggregate sum. This
sum is called the datasum. The overflow carries generated by the
additions are either discarded or added back into the datasum. The
transmitting device then sends a form of this datasum appended to the
end of the block. This new form of the datasum is called the checksum.
As the data elements are received, they are added a second time in
order to recreate the datasum. Once all of the data elements have been
received, the receiving device compares its calculated datasum with the
checksum sent by the transmitting device. The data is considered error
176 Computer Organization and Design Fundamentals
free if the receiving device's datasum compares favorably with the
transmitted checksum. Figure 9-7 presents a sample data block and the
datasums generated both by discarding the two carries and by adding
the carries to the datasum.
Datasum Datasum
(discarded (added
Data
carries) carries)
Upon receiving this transmission, the datasum for this data block
must be calculated. Begin by taking the sum of all the data elements.
As shown earlier, the basic checksum for the data block in Figure
9-7 is 5916 (010110012). The 1's complement checksum for the same
data block is equal to the 1's complement of 5916.
The 2's complement checksum for the data block is equal to the 2's
complement of 5916.
Example
Determine if the data block and accompanying checksum below are
error free. The data block uses a 1's complement checksum.
Data Checksum
0616 0016 F716 7E16 0116 5216 3116
Solution
First, calculate the datasum by adding all the data elements in the
data block.
CE16 = 110011102
1's complement of CE16 = 0011000012 = 3116
Example
Write a C program to determine the basic checksum, 1's complement
checksum, and 2's complement checksum for the data block 0716, 0116,
2016, 7416, 6516, 6416, 2E16.
Solution
Before we get started on this code, it is important to know how to
take a 1's complement and a 2's complement in C. The 1's complement
uses a bitwise not operator '~'. By placing a '~' in front of a variable or
constant, the bitwise inverse or 1's complement is returned. Since most
computers represent negative numbers with 2's complement notation,
the 2's complement is calculated by placing a negative sign in front of
the variable or constant.
The code below begins by calculating the datasum. It does this with
a loop that adds each value from the array of data values to a variable
labeled datasum. After each addition, any potential carry is stripped off
using a bitwise AND with 0xff. This returns the byte value.
Once the datasum is calculated, the three possible checksum values
can be calculated. The first one is equal to the datasum, the second is
equal to the bitwise inverse of the datasum, and the third is equal to the
2's complement of the datasum.
int datasum=0;
int block[] = {0x07, 0x01, 0x20, 0x74,
0x65, 0x64, 0x2E};
This is not a robust example due to the fact that 4 bits only have 16
possible bit patterns, but the result is clear. A single bit change in one
of the data elements resulted in a single bit change in the addition
result. The same change, however, resulted in three bits changing in the
division remainder.
The problem is that division in binary is not a quick operation. For
example, Figure 9-9 shows the long division in binary of 31,45210 =
01111010110111002 by 910 = 10012. The result is a quotient of
1101101001102 = 3,49410 with a remainder of 1102 = 610.
Remember that the goal is to create a checksum that can be used to
check for errors, not to come up with a mathematically correct result.
Keeping this in mind, the time it takes to perform a long division can be
reduced by removing the need for "borrows". This would be the same
as doing an addition while ignoring the carries. The truth table in Table
9-2 shows the single bit results for both addition and subtraction when
carries and borrows are ignored.
Chapter 9: Binary Operation Applications 181
110110100110
1001 0111101011011100
-1001
1100
-1001
1110
-1001
1011
-1001
1010
-1001
1111
-1001
1100
-1001
110
A B A+B A–B
0 0 0 0
0 1 1 1 (no borrow)
1 0 1 1
1 1 0 (no carry) 0
11011010 11011010
+01101100 -01101100
10110110 10110110
111010001010
1001 0111101011011100
-1001
1100
-1001
1011
-1001
1001
-1001
01011
-1001
1010
-1001
110
Example
Perform the long division of 11001101101010112 by 10112 in binary
using the borrow-less subtraction, i.e., XOR function.
Solution
Using the standard "long-division" procedure with the XOR
subtractions, we divide 10112 into 11001101101010112. Table 9-4
checks our result using the technique shown in Table 9-3. Since we
were able to recreate the original value from the quotient and
remainder, the division must have been successful.
Note that in Table 9-4 we are reconstructing the original value from
the quotient in order to demonstrate the application of the XOR in this
modified division and multiplication. This is not a part of the CRC
implementation. In reality, as long as the sending and receiving devices
use the same divisor, the only result of the division that is of concern is
the remainder. As long as the sending and receiving devices obtain the
same results, the transmission can be considered error free.
184 Computer Organization and Design Fundamentals
1110101001111
1011 1100110110101011
-1011
1111
-1011
1001
-1011
1001
-1011
1010
-1011
1101
-1011
1100
-1011
1111
-1011
1001
-1011
010
Solution
With a 5 bit divisor, append 5 – 1 = 4 zeros to the end of the data.
1100001010110010
11011 10110111100101100000
-11011
11011
-11011
011100
-11011
11110
-11011
10111
-11011
11000
-11011
11000
-11011
0110
The data stream sent to the receiving device becomes the original
data stream with the 4-bit remainder appended to it.
If the receiver divides the entire data stream by the same divisor
used by the transmitting device, i.e., 110112, the remainder will be zero.
This is shown in the following division. If this process is followed, the
receiving device will calculate a zero remainder any time there is no
error in the data stream.
Chapter 9: Binary Operation Applications 187
1100001010110010
11011 10110111100101100110
-11011
11011
-11011
011100
-11011
11110
-11011
10111
-11011
11000
-11011
11011
-11011
00
Table 9-5 Data Groupings and Parity for the Nibble 10112
In memory, the nibble would be stored with its parity bits in an eight-
bit location as 101101002.
Now assume that the bit in the D1 position which was originally a 1
is flipped to a 0 causing an error. The new value stored in memory
would be 100101002. Table 9-6 duplicates the groupings of Table
9-5 with the new value for D1. The table also identifies groups that
incur a parity error with the data change.
Note that parity is now in error for groups A, C, and D. Since the D1
position is the only bit that belongs to all three of these groups, then a
Chapter 9: Binary Operation Applications 191
processor checking for errors would not only know that an error had
occurred, but also in which bit it had occurred. Since each bit can only
take on one of two possible values, then we know that flipping the bit
D1 will return the nibble to its original data.
If an error occurs in a parity bit, i.e., if P3 is flipped, then only one
group will have an error. Therefore, when the processor checks the
parity of the four groups, a single group with an error indicates that it is
a parity bit that has been changed and the original data is still valid.
It turns out that not all four data groupings are needed. If we only
use groups A, B, and C, we still have the same level of error detection,
but we do it with one less parity bit. Continuing our example without
Group D, if our data is error-free or if a single bit error has occurred,
one of the following eight situations is true.
Circle A Circle B
P0 D1 P1
D3
D2 D0
Circle C P2
Figure 9-13a uses this arrangement to insert the nibble 10112 into a
Venn diagram. Figures 9-13b, c, and d show three of the seven possible
error conditions.
A B A B
0 1 1 1 1 1
1 1
0 1 0 1
0 0
C C
A B A B
0 1 1 0 1 1
1 0
1 1 0 1
0 0
C C
A B A B
0 1 1 0 1 0
1 1
0 1 0 0
0 0
C C
This can be done by adding one more bit that acts as a parity check
for all seven data and parity bits. Figure 9-15 represents this new bit
using the same example from Figure 9-14.
If a single-bit error occurs, then after we go through the process of
correcting the error, this new parity bit will be correct. If, however,
after we go through the process of correcting the error and the new
parity bit is in error, then it can be assumed that a double-bit error has
occurred and that correction is not possible. This is called Single-Error
Correction/Doubled-Error Detection.
194 Computer Organization and Design Fundamentals
A B A B
0 1 1 0 1 0
1 1
0 1 0 0
0 0
C 0 New parity C 0
bit
a.) Error-free condition b.) Two-Bit Error Condition
account for the condition where there are no errors. If 2p – 1 is less than
the number of data bits, n, plus the number of parity bits, p, then we
don't have enough parity bits. This relationship is represented with
equation 9-1.
p + n < 2p – 1 (9.1)
Table 9-9 presents a short list of the number of parity bits that are
required for a specific number of data bits. To detect double-bit errors,
an additional bit is needed to check the parity of all of the p + n bits.
Let's develop the error-checking scheme for 8 data bits. Remember
from the four-bit example that there were three parity checks:
• P0 was the parity bit for data bits for D1, D2, and D3;
• P1 was the parity bit for data bits for D0, D1, and D3; and
• P2 was the parity bit for data bits for D0, D2, and D3.
Chapter 9: Binary Operation Applications 195
Table 9-9 Parity Bits Required for a Specific Number of Data Bits
Number of Number of p + n 2p – 1
data bits (n) parity bits (p)
4 3 7 7
8 4 12 15
16 5 21 31
32 6 38 63
64 7 71 127
128 8 136 255
In order to check for a bit error, the sum of ones for each of these
groups is taken. If all three sums result in even values, then the data is
error-free. The implementation of a parity check is done with the XOR
function. Remember that the XOR function counts the number of ones
at the input and outputs a 1 for an odd count and a 0 for an even count.
This means that the three parity checks we use to verify our four data
bits can be performed using the XOR function. Equations 9.2, 9.3, and
9.4 show how these three parity checks can be done. The XOR is
represented here with the symbol ⊕.
The single parity bit error reveals itself as a single parity check
outputting a 1. If, however, a data bit changed, then we have more than
one parity check resulting in a 1. Assume, for example, that D1 changed
from a 1 to a 0.
Since D1 is the only bit that belongs to both the parity check of
groups A and B, then D1 must have been the one to have changed.
Using this information, we can go to the eight data bit example.
With four parity bits, we know that there will be four parity check
equations, each of which will have a parity bit that is unique to it.
The next step is to figure out which data bits, D0 through D7, belong
to which groups. Each data bit must have a unique membership pattern
so that if the bit changes, its parity check will result in a unique pattern
of parity check errors. Note that all of the data bits must belong to at
least two groups to avoid an error with that bit looking like an error
with the parity bit.
Table 9-10 shows one way to group the bits in the different parity
check equations or groups. It is not the only way to group them.
By using the grouping presented in Table 9-10, we can complete our
four parity check equations.
When it comes time to store the data, we will need 12 bits, eight for
the data and four for the parity bits. But how do we calculate the parity
bits? Remember that the parity check must always equal zero.
Therefore, the sum of the data bits of each parity group with the parity
bit must be an even number. Therefore, if the sum of the data bits by
themselves is an odd number, the parity bit must equal a 1, and if the
sum of the data bits by themselves is an even number, the parity bit
must equal a 0. This sounds just like the XOR function again.
Therefore, we use equations 9.9, 9.10, 9.11, and 9.12 to calculate the
parity bits before storing them.
P0 = D0 ⊕ D1 ⊕ D3 ⊕ D4 ⊕ D6 (9.9)
P1 = D0 ⊕ D2 ⊕ D3 ⊕ D5 ⊕ D6 (9.10)
P2 = D1 ⊕ D2 ⊕ D3 ⊕ D7 (9.11)
P3 = D4 ⊕ D5 ⊕ D6 ⊕ D7 (9.12)
Now let's test the system. Assume we need to store the data
100111002. This gives us the following values for our data bits:
D 7 = 1 D 6 = 0 D 5 = 0 D4 = 1 D 3 = 1 D 2 = 1 D 1 = 0 D0 = 0
198 Computer Organization and Design Fundamentals
The first step is to calculate our parity bits. Using equations 9.9,
9.10, 9.11, and 9.12 we get the following values.
P0 = 0 ⊕ 0 ⊕ 1 ⊕ 1 ⊕ 0 = 0
P1 = 0 ⊕ 1 ⊕ 1 ⊕ 0 ⊕ 0 = 0
P2 = 0 ⊕ 1 ⊕ 1 ⊕ 1 = 1
P3 = 1 ⊕ 0 ⊕ 0 ⊕ 1 = 0
Once again, the XOR is really just a parity check. Therefore, if there
is an odd number of ones, the result is 1 and if there is an even number
of ones, the result is 0.
Now that the parity bits have been calculated, the data and parity
bits can be stored together. This means that memory will contain the
following value:
D7 D6 D5 D4 D3 D2 D1 D0 P0 P1 P2 P3
1 0 0 1 1 1 0 0 0 0 1 0
If our data is error free, then when we read it and substitute the
values for the data and parity bits into our parity check equations, all
four results should equal zero.
Parity check A = 0 ⊕ 0 ⊕ 0 ⊕ 1 ⊕ 1 ⊕ 0 = 0
Parity check B = 0 ⊕ 0 ⊕ 1 ⊕ 1 ⊕ 0 ⊕ 0 = 0
Parity check C = 1 ⊕ 0 ⊕ 1 ⊕ 1 ⊕ 1 = 0
Parity check D = 0 ⊕ 1 ⊕ 0 ⊕ 0 ⊕ 1 = 0
If, however, while the data was stored in memory, it incurs a single-
bit error, e.g., bit D6 flips from a 0 to a 1, then we should be able to
detect it. If D6 does flip, the value shown below is what will be read
from memory, and until the processor checks the parity, we don't know
that anything is wrong with it.
D7 D6 D5 D4 D3 D2 D1 D0 P0 P1 P2 P3
1 1 0 1 1 1 0 0 0 0 1 0
Start by substituting the values for the data and parity bits read from
memory into our parity check equations. Computing the parity for all
four groups shows that an error has occurred.
Chapter 9: Binary Operation Applications 199
Parity check A = 0 ⊕ 0 ⊕ 0 ⊕ 1 ⊕ 1 ⊕ 1 = 1
Parity check B = 0 ⊕ 0 ⊕ 1 ⊕ 1 ⊕ 0 ⊕ 1 = 1
Parity check C = 1 ⊕ 0 ⊕ 1 ⊕ 1 ⊕ 1 = 0
Parity check D = 0 ⊕ 1 ⊕ 0 ⊕ 1 ⊕ 1 = 1
Since we see from Table 9-10 that the only bit that belongs to parity
check groups A, B, and D is D6, then we know that D6 has flipped and
we need to invert it to return to our original value.
The same problem appears here as it did in the nibble case if there
are two bit errors. It is solved here the same way as it was for the nibble
application. By adding a parity bit representing the parity of all twelve
data and parity bits, then if one of the group parities is wrong but the
overall parity is correct, we know that a double-bit error has occurred
and correction is not possible.
Problems
1. Using an original value of 110000112 and a mask of 000011112,
calculate the results of a bitwise AND, a bitwise OR, and a bitwise
XOR for these values.
2. Assume that the indicators of an automotive dashboard are
controlled by an 8-bit binary value named dash_lights. The table
below describes the function of each bit. Assume that a '1' turns on
the light corresponding to that bit position and a '0' turns it off.
D0 Low fuel D4 Left turn signal
D1 Oil pressure D5 Right turn signal
D2 High temperature D6 Brake light
D3 Check engine D7 Door open
For each of the following situations, write the line of code that uses
a bitwise operation to get the desired outcome.
200 Computer Organization and Design Fundamentals
a.) Turn on the low fuel, oil pressure, high temperature, check
engine, and brake lights without affecting any other lights. This
would be done when the ignition key is turned to start.
b.) Toggle both the right and left turn signals as if the flashers
were on without affecting any other lights.
c.) Turn off the door open light when the door is closed.
18. Identify the error in the parity check equations below. Note that the
expressions are supposed to represent a different grouping than
those in equations 9.2, 9.3, and 9.4. There is still an error though
with these new groupings.
Parity check for group A = P0 ⊕ D0 ⊕ D2 ⊕ D3
Parity check for group B = P1 ⊕ D0 ⊕ D1
Parity check for group C = P2 ⊕ D1 ⊕ D2