'Assembly code to find out if a number is perfect or not
I am trying to write a program that can determine if a number is perfect or not.
The program reports the number 6 to be perfect, but for the number 28, which is also a perfect number, it tells that it is not perfect.
Can you help?
.data
msg1 db 13,10," Enter a number:$"
perf db 13,10,"The number is perfect number $"
nperf db 13,10,"The number is not perfect number $"
num dw ?
x dw 0
.const
.code
ppp proc _x$:word
uses bx,dx,ax,cx
mov bx,0
mov ax,_x$
mov cx,ax
dec cx
cmp ax,1
je perfecto
next:
cwd
div cx
cmp dx,0
je sum
dec cx
jmp next
sum:
add bx,cx
dec cx
cmp cx,0
jnz next
mov ax,_x$
cmp ax,bx
je perfecto
call puts,offset nperf
jmp done
perfecto:
call puts,offset perf
done:
ret
ppp endp
.startup
call puts,offset msg1
call endl
call getint
mov num,ax
call ppp,num
finish2:
.exit
end
Solution 1:[1]
cmp ax,1 je perfecto
Why do you consider 1 to be a perfect number? The wikipedia article https://en.wikipedia.org/wiki/Perfect_number uses the following definition:
In number theory, a perfect number is a positive integer that is equal to the sum of its positive divisors, excluding the number itself. For instance, 6 has divisors 1, 2 and 3 (excluding itself), and 1 + 2 + 3 = 6, so 6 is a perfect number.
The number 1 has but one divisor (1) and that's the one that the definition wants you to exclude. What remains is nothing and that is clearly not equal to the number 1.
Moreover that Wikipedia article also mentions that there probably are no odd perfect numbers.
Why the code fails
The idea behind your code is to divide the inputted number N by all of the numbers in the range [N-1, 1]. Upon finding a zero remainder, you add the current divisor to your sum variable BX. This is not optimal, but it can work. However, because you neglect to reload the original number, you are not dividing the inputted number but rather the quotient that you got from the previous division. It seemingly works for the number 6 because its 3 divisors follow each other closely and are the final 3 iterations of the loop.
AX DX
6 / 5 -> Q=1 R=1
1 / 4 -> Q=0 R=1
0 / 3 -> Q=0 R=0 -> BX=0+3=3
0 / 2 -> Q=0 R=0 -> BX=3+2=5
0 / 1 -> Q=0 R=0 -> BX=5+1=6 OK
Now consider 28:
AX DX
28 / 27 -> Q=1 R=1
1 / 26 -> Q=0 R=1
0 / 25 -> Q=0 R=0 -> BX=0+25=25
0 / 24 -> Q=0 R=0 -> BX=25+24=49
0 / 23 -> Q=0 R=0 -> BX=49+23=72
0 / 22 -> Q=0 R=0 -> BX=72+22=94
0 / 21 -> Q=0 R=0 -> BX=94+21=115 ???
...
A solution
- Better start dividing at half the inputted number. Larger divisors will never produce a zero remainder.
- Better no do that final division by 1. It will always produce a zero remainder. Just
inc bxin the end. Or even better initialize the running sum at 1 instead of 0. - Better write the division loop with a single conditional branch instruction instead of a conditional exit branch and an unconditional repeat branch.
mov bx, 1 ; Running sum
mov cx, _x$ ; Inputted number
shr cx, 1 ; First divisor is N/2
jz NotPerfect ; N=0 or N=1
next:
mov ax, _x$ ; RELOADING NUMBER
xor dx, dx
div cx
dec cx ; (*)
test dx, dx
jnz next
add bx, cx
inc bx ; (*) Because CX got already decremented
cmp cx, 1
ja next
AX DX
28 / 14 -> Q=2 R=0 -> BX=1+(13+1)=15
28 / 13 -> Q=2 R=2
28 / 12 -> Q=2 R=4
28 / 11 -> Q=2 R=6
28 / 10 -> Q=2 R=8
28 / 9 -> Q=2 R=10
28 / 8 -> Q=3 R=4
28 / 7 -> Q=4 R=0 -> BX=15+(6+1)=22
28 / 6 -> Q=4 R=4
28 / 5 -> Q=5 R=3
28 / 4 -> Q=7 R=0 -> BX=22+(3+1)=26
28 / 3 -> Q=9 R=1
28 / 2 -> Q=14 R=0 -> BX=26+(1+1)=28 OK
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Sep Roland |
