patternCritical
Decide if the sum of three numbers is even or odd
Viewed 0 times
threethenumbersdecidesumevenodd
Problem
Exercise from an Assembly course I'm enrolled into:
Write a program that takes three numbers x,y,z as input, and returns:
-
0 if (x+y+z) is even.
-
1 if (x+y+z) is odd.
(Note that here + means arithmetic addition). Do it without actually
adding the numbers. HINT: Use the XOR instruction.
Full exercise-sheet here: GitHub
Here's the code I've written. Please mind the comments I've added for to explain my ideas.
I'm not completely sure if I have gotten the exercise-task correctly. But let's assume that the entered three numbers have to be summed up and that one has to use xor in some way.
How could I improve my solution? Is there a better solution?
Write a program that takes three numbers x,y,z as input, and returns:
-
0 if (x+y+z) is even.
-
1 if (x+y+z) is odd.
(Note that here + means arithmetic addition). Do it without actually
adding the numbers. HINT: Use the XOR instruction.
Full exercise-sheet here: GitHub
Here's the code I've written. Please mind the comments I've added for to explain my ideas.
format PE console
entry start
include 'win32a.inc'
; ===============================================
section '.text' code readable executable
start:
call read_hex ; Provided by the teacher. Reads a hexadecimal number from stdin.
mov ebx, eax
call read_hex
add ebx, eax
call read_hex
add ebx, eax
mov eax, ebx ; Move the sum of the three given numbers into eax.
xor eax, 0x1 ; If the number is odd then the lowest bit becomes 0. If the number is even then the lowest bit becomes 1.
ror eax, 0x1 ; Rotate the lowest bit out of the register. So that the carry-flag gets a new state.
jc evenNumber ; jc == Jump if the carry-flag has become 1. Means the last bit has been a 1, which in turn means that the number is even.
mov eax, 0x1 ; Otherwise just write 0 into eax, which signals an odd number. And prints this to stdout.
jmp print_result
evenNumber:
mov eax, 0x0
print_result:
call print_eax_binary ; Provided by the teacher. Prints a binary number to stdout.
; Exit the process:
push 0
call [ExitProcess]
include 'training.inc'I'm not completely sure if I have gotten the exercise-task correctly. But let's assume that the entered three numbers have to be summed up and that one has to use xor in some way.
How could I improve my solution? Is there a better solution?
Solution
First off, you have nice, clean, well-formatted, easy-to-read code. You have even included comments that explain the goal of each instruction. Too much of the time I review assembly-language code, these are the things that go wrong. You've gotten them all correct. Nice job! Now I don't have to pull my hair out trying to read and understand your code.
Unfortunately, were I your instructor, I'd still have to give you a failing score on the assignment because you didn't follow the rules. The assignment says that you must "Do it without actually adding the numbers.", but right off the bat, you
Now, personally, I think these types of assignments are rather silly, so I wouldn't be giving them. If I wanted you to learn how to use the bitwise operators, I'd find something useful and real-world that they are good for, and then give you that assignment. It's not like I'd have to work very hard. The chip designers didn't put them in merely for fun.
Oh well; you have to do the assignment that you were given. So follow the hint, use
For example:
Notice that each column follows the truth table for an XOR operation, which basically just says that the output is 1 (true) whenever the inputs differ. That's the "exclusive" part of the OR.
Eventually, while doing this preliminary research, I suspect you'd come across someone talking about how XOR operations are used to implement parity checks. Mast's answer here already spilled the beans. Parity indicates whether an integer is even or odd, and can be calculated simply by a XOR sum of the bits. Judging from your comments in the code, you already know that for a binary number, the lowest (least significant) bit determines whether it is odd or even.
So the good news is that your code is almost entirely correct. If you change the
Now we've XOR-summed all of the bits from the three inputs. All that's left is figuring out the parity of the result—i.e., whether the XOR-sum is odd or even.
You've already implemented one way of doing that, but as Quuxplusone pointed out, it is an unnecessarily complicated way. While it doesn't always hold when you start getting into more advanced things, for simple arithmetic operations, fewer instructions means faster code. Arguably more importantly, it means simpler code, which is more likely to be correct code. Moreover, fewer branches virtually always mean faster code, and certainly code whose flow of execution is easier to follow, and thus easier to debug.
I'd disagree slightly with Quuxplusone here and say that clever is totally fine, as long as your cleverness has some notable advantage. You don't always have to write code the "normal" way, because the "normal" way might be sub-optimal. Generally, if we're dropping down to write in assembly, it's because we want to write the best code we possibly can (either fastest, shortest, or whatever metric we are using to judge "best"), which means that "normal" isn't necessarily an important goal. Sometimes, "readable" isn't even an important goal. But, by the same token, I do agree there's no point in deviating from what is normal if your deviation is inferior, and that's certainly the case here.
Your XOR-sum is in the
Oh, and finally, since we need the result in
(We could have just as easily done the
Go
Unfortunately, were I your instructor, I'd still have to give you a failing score on the assignment because you didn't follow the rules. The assignment says that you must "Do it without actually adding the numbers.", but right off the bat, you
ADD the input values together. And we were off to such a good start… :-(Now, personally, I think these types of assignments are rather silly, so I wouldn't be giving them. If I wanted you to learn how to use the bitwise operators, I'd find something useful and real-world that they are good for, and then give you that assignment. It's not like I'd have to work very hard. The chip designers didn't put them in merely for fun.
Oh well; you have to do the assignment that you were given. So follow the hint, use
XOR. Maybe you don't know exactly what that means, so what I'd do is open up a programmer's calculator (all major desktop operating systems have a "programmer" mode for their calculators) and play around with it. Pick random combinations of positive and negative numbers, and compare what the results are when you add them together versus when you XOR them together. Try to get a feel for what XOR does. Then, look up a formal definition of XOR (exclusive-OR). If you're like me, your eyes glaze over at the symbolic logic stuff (which wasn't so great when I took that course in college); feel free to skip over that for the purposes of this assignment. Your real goal here is to find out what XOR actually does at the bit level. There are lots of detailed explanations of bitwise manipulation online, or you may have a textbook that covers this stuff, too.For example:
01101101
XOR 11010100
_____________
10111001Notice that each column follows the truth table for an XOR operation, which basically just says that the output is 1 (true) whenever the inputs differ. That's the "exclusive" part of the OR.
Eventually, while doing this preliminary research, I suspect you'd come across someone talking about how XOR operations are used to implement parity checks. Mast's answer here already spilled the beans. Parity indicates whether an integer is even or odd, and can be calculated simply by a XOR sum of the bits. Judging from your comments in the code, you already know that for a binary number, the lowest (least significant) bit determines whether it is odd or even.
So the good news is that your code is almost entirely correct. If you change the
ADDs to XORs, it will follow the rules of the assignment and produce the correct result. Thus, you would have:call read_hex ; Provided by the teacher. Reads a hexadecimal number from stdin.
mov ebx, eax
call read_hex
xor ebx, eax
call read_hex
xor ebx, eaxNow we've XOR-summed all of the bits from the three inputs. All that's left is figuring out the parity of the result—i.e., whether the XOR-sum is odd or even.
You've already implemented one way of doing that, but as Quuxplusone pointed out, it is an unnecessarily complicated way. While it doesn't always hold when you start getting into more advanced things, for simple arithmetic operations, fewer instructions means faster code. Arguably more importantly, it means simpler code, which is more likely to be correct code. Moreover, fewer branches virtually always mean faster code, and certainly code whose flow of execution is easier to follow, and thus easier to debug.
I'd disagree slightly with Quuxplusone here and say that clever is totally fine, as long as your cleverness has some notable advantage. You don't always have to write code the "normal" way, because the "normal" way might be sub-optimal. Generally, if we're dropping down to write in assembly, it's because we want to write the best code we possibly can (either fastest, shortest, or whatever metric we are using to judge "best"), which means that "normal" isn't necessarily an important goal. Sometimes, "readable" isn't even an important goal. But, by the same token, I do agree there's no point in deviating from what is normal if your deviation is inferior, and that's certainly the case here.
Your XOR-sum is in the
EBX register. You know that the XOR-sum tells you the parity in the least-significant bit. So what is the obvious thing to do? Mask off everything but the least-significant bit, and that'll be your answer! How do we mask off bits? Use a logical AND operation:and ebx, 1 ; mask off all but the low-order bitOh, and finally, since we need the result in
EAX, we'll do:mov eax, ebx(We could have just as easily done the
MOV first, before the AND. It doesn't matter.)Go
Code Snippets
01101101
XOR 11010100
_____________
10111001call read_hex ; Provided by the teacher. Reads a hexadecimal number from stdin.
mov ebx, eax
call read_hex
xor ebx, eax
call read_hex
xor ebx, eaxand ebx, 1 ; mask off all but the low-order bitmov eax, ebxcall read_hex ; Provided by the teacher. Reads a hexadecimal number from stdin into EAX.
mov ebx, eax
call read_hex
xor ebx, eax
call read_hex
xor ebx, eax
mov eax, ebx ; Move XOR-sum of three given numbers into EAX.
and eax, 0x1 ; Mask off all but the lowest-order bit.
call print_eax_binary ; Provided by the teacher. Prints a binary number in EAX to stdout.
; Exit the process:
push 0
call [ExitProcess]Context
StackExchange Code Review Q#163370, answer score: 61
Revisions (0)
No revisions yet.