Check if a number is odd or even (mathematical parity)
Asked Answered
S

5

5

For a one digit number, I want to know if it's odd or even (multiple of 2). For example, given 9, print that is an odd number.

(i.e. check the mathematical parity, not the computing parity.)

This is what I have:

   assume cs:cseg,ds:dseg,ss:sseg
    cseg segment
    start:  
    mov ax, dseg
    mov ds, ax  
    
    mov ah, 01h ; Here, im adding a number
    int 21h 
    
    
    
    jp even
    jnp odd  
    
    even: 
    mov ah,09
    lea dx,par
    int 21h  
    jmp exit
    odd: 
    mov ah,09
    lea dx,odd1 
    int 21h   
    jmp salir
    salir:
    mov ax,4C00h
    int 21h
    
    cseg ends 
    
    dseg segment byte         
    even Db 'Even number$'
    odd11 Db 'Odd number$'
    dseg ends  
    
    sseg segment stack
    db 100h dup(?)
    sseg ends

end start
Striated answered 27/3, 2015 at 2:45 Comment(0)
U
7

To test if a number is odd or even, you check bit 0 of this number. if bit0 is set, then the number is odd. nothing else. Don't confuse PF (parity flag) and JP/JNP conditions. Parity Flag shows if the number of bits set in the least significant byte is even or odd.

From documentation:

PF (bit 2) Parity flag — Set if the least-significant byte of the result contains an even number of 1 bits; cleared otherwise.

Number 1 - odd number, 00000001b PF is cleared because there's only one bit set
Number 2 - even number, 00000010b, but PF is cleared again! because there's only one bit set
Number 3 - odd number, 00000011b, but PF set! because there're two bits set

Urushiol answered 27/3, 2015 at 10:32 Comment(2)
@TheLogicGuy: test eax, 1Bifarious
Or better, test al, 1 because there's no test r32, imm8 encoding, so it's shorter to only read the low byte of eax by reading al. (Writing partial registers is bad, reading them is fine.)Bifarious
B
4

While Alexander Zhak's answer pointed out the flaws in your code checking the parity flag, the fastest way to check the parity of the number in al in a non-desctructive way is:

        test al,1
        jz even
odd:    ...
even:   ...
Befoul answered 30/3, 2015 at 13:59 Comment(0)
A
3

To test if a number is odd or even, just shift it right once and check the carry flag:

    mov     al, 09
    shr     al       ; shifts all bits right 1, lowest bit to carry flag
    jc      _odd     ; carry set - there was a bit in lowest position
_even:
    ; Do something with even numbers and exit
_odd:
    ; Do something with odd numbers and exit

Understanding the carry flag is very useful for a whole variety of coding tricks. For instance, "Parity" is counting up the total number of bits in the number and we can use a similar trick for that:

    mov    ah, 09
    xor    al, al    ; al = 0
_loop:
    shr    ah        ; lower bit into carry flag
    adc    al, 0     ; Add 0 + carry flag to al
    and    ah, ah    ; sets Z flag if ah is zero
    bne    _loop
    ;  al now contains the total number of bits contained in ah
Anele answered 27/3, 2015 at 8:47 Comment(6)
minus for "just shift it right once and check the carry flag" which destroys data which is being checkedUrushiol
He just wants to know whether it's odd or even, not re-use it. 1 instruction byte and no memory access overhead is the most efficient way of checking if you aren't going to use the value again (which he isn't in his code).Anele
@Mike: test al,1 / jnz _odd is significantly more efficient than a shift. On Intel since Core2 and AMD since Bulldozer, that can macro-fuse into a single test-and-branch uop. shr al and test al,1 are both 2-byte instructions. Your loop is also less efficient than it could be. First test ah,ah would be better than and. Second, you can arrange it so you branch on ZF as set by shr. (e.g. with adc al,0 / shr ah / jnz _loop / adc al,0. On the first iteration, adc al,0 is a no-op because the xor al,al cleared CF. After exiting the loop, you add the last bit's CF.Bifarious
See agner.org/optimize and other x86 performance links in the x86 tag wikiBifarious
@Mike: you claim that shr al is 1 instruction byte (which would be faster on 8088). It's 2 bytes, same as test al,1; there is no special case AL / AX no-modrm encoding for shifts, only other instructions that actually take an immediate like test. (Variable-count shifts were new in 186, and no short-form exists for them either, but if one did it would still be 2 total bytes.)Bifarious
Also, the computing definition of parity is just whether the popcount is odd or even, not the actual total. So test al,al / jp. Or for the whole AX, xor al,ah / jp. No need to do a popcount loop unless you actually want the total, and don't have the popcnt instruction available.Bifarious
S
0

I found the solution!

Just add "and al,00000001" above of jp even

assume cs:cseg,ds:dseg,ss:sseg
    cseg segment
    start:  
    mov ax, dseg
    mov ds, ax  

    mov ah, 01h ; Here, im adding a number
    int 21h 

and al,00000001

    jp even
    jnp odd  

    even: 
    mov ah,09
    lea dx,par
    int 21h  
    jmp exit
    odd: 
    mov ah,09
    lea dx,odd1 
    int 21h   
    jmp salir
    salir:
    mov ax,4C00h
    int 21h

    cseg ends 

    dseg segment byte         
    even Db 'Even number$'
    odd11 Db 'Odd number$'
    dseg ends  

    sseg segment stack
    db 100h dup(?)
    sseg ends

end start
Striated answered 27/3, 2015 at 3:3 Comment(2)
1 - 0000_0001 an odd number, parity odd. 2 - 0000_0010 an even number, parity odd. 3 - 0000_0011 an odd number, parity even. What are you actually trying to do?Insincere
Better: test al, 1 instead of and al,1. Same instruction length, but can macro-fuse with jcc on modern CPUs. (Even jp, although the parity of a single bit is the same as testing whether it's zero, which would be easier to understand and just as fast, although this does work.)Bifarious
S
-2
    .MODEL SMALL
 .STACK 100H

 .DATA
   PROMPT_1  DB  'Enter a number: $'
   PROMPT_2  DB  0DH,0AH,'The given number in binary is: $'
   PROMPT_3  DB  0DH,0AH,'$' 
   MSG1 DB 10,13,’Number is Odd $’
   MSG2 DB 10,13,’Number is Even $’

 .CODE
   MAIN PROC
     MOV AX, @DATA                
     MOV DS, AX

     LEA DX, PROMPT_1                
     MOV AH, 9
     INT 21H

     XOR BL, BL                  
     MOV CX, 8                   
     MOV AH, 1                   

     @INPUT:                     
       INT 21H                   
       CMP AL, 0DH                
       JE @END                   
       AND AL, 0FH                
       SHL BL, 4                 
       OR BL, AL                  
     LOOP @INPUT                  

     @END:                        

     MOV AL, BL                   
     MOV CX, 8                    

     @LOOP:                       
       SHR AL, 1                  
       RCL BL, 0                  
     LOOP @LOOP                   

     LEA DX, PROMPT_2             
     MOV AH, 9
     INT 21H

     MOV CX, 8                    
     MOV AH, 2                    

     @OUTPUT:                     
       SHL BL, 1                  

       JNC @ZERO                  
         MOV DL, 31H              
         JMP @DISPLAY             

       @ZERO:                     
         MOV DL, 30H              

       @DISPLAY:                  
         INT 21H                  
     LOOP @OUTPUT                      



MOV AH,01H
INT 21H 

mov ah,01h


SAR AL,01h
JC ODD


LEA SI,MSG1
CALL PRINT

JMP TERMINATE

ODD:
LEA SI,MSG2
CALL PRINT


TERMINATE:
MOV AH,4CH
INT 21H

PRINT PROC
MOV DX,SI
MOV AH,09H
INT 21H




     MOV AH, 4CH                 
     INT 21H

 END MAIN
Seer answered 15/9, 2017 at 7:55 Comment(1)
What does this do, and why is it a useful answer to this question? Is shr al,1 / rcl bl, 0 / loop supposed to bit-reverse AL? It doesn't; rcl with a count of zero is an expensive no-op, and isn't supported on 8086. rcl bl, 1 works on 8086, and would make a correct bit-reverse loop. Anyway, you only need to look at the last decimal digit of a multi-digit number to decide if it's odd or even, so you're way overcomplicating this (whatever this is...)Bifarious

© 2022 - 2024 — McMap. All rights reserved.