Determine number of negative and positive numbers in an array

Deadly 提交于 2019-12-13 07:38:19

问题


I need to determine the number of negative and positive numbers in an array in assembler. It seems that the assembler doesn't recognizes them as negative numbers. How can I solve this problem? I define the array this way:

word_array db 3, -2, 11, -1, -2, -7, -5, -20

I have this function that counts the positive ones:

count_positives:
mov dx, word [word_array + 2*ecx - 2]
cmp edx, 0
JL skip
inc ebx
skip:
loopnz count_positives

回答1:


Count either negative or non-negative, and subtract that from the total count to get the other. If you need to count negative and positive, then you need two counters, and a test followed by two branches (so that zero doesn't go into either counter).

Adapted from Sten's answer, but with some improvements.

section .rodata

word_array dw -1,2,-3,4
len  equ $-word_array     ; length in bytes.  assembler constant, so we can mov reg, imm8/imm32   rather than loading it as data.

section .text
proc:
  mov   esi, word_array  ; esi points to the array.  In MASM, use OFFSET word_array
  mov   ecx, len/2 - 1      ; [esi + ecx*2] points to the last element
  xor   edx, edx           ; non_neg_count = 0

countloop:
    ; cmp   [esi + ecx*2], 0   ; This can't macro-fuse (memory and immediate operand).  Also can't micro-fuse on SnB, because of a 2-reg addressing mode
  movsx   eax, word [esi + ecx*2]  ; use a 2-reg addressing mode to save loop overhead, since this there's no ALU execution port component to this insn.  It doesn't need to micro-fuse to be one uop
  test    eax, eax        ; can macro-fuse with js
  js isNegative
  inc   edx               ; counting non-negative numbers
isNegative:
  dec   ecx               ; can macro-fuse with jge, but probably won't unless alignment stops it from being decoded in the same cycle as the earlier test/js
  jge countloop       ; jge, not jnz, because we want ecx from [0 : len-1], rather than [1 : len]

; after the loop, ecx=-1, edx=non_neg_count
; neg_count = array_count - non_neg_count
  mov   eax, len/2
  sub   eax, edx        ;   eax =  neg_count

  ret    ; return values in eax:edx

The loop is 4 uops on Intel. (Or more likely 5, if both test/branch pairs hit the decoders in the same cycle so only one macro-fuses).

A branch-less version with sets bl / add edx, ebx might work well.

You could maybe save slightly on code size by zeroing eax, then using scasw in a loop to compare ax with [esi], and increment esi by two, but it's not a good choice if you aren't using it with a rep prefix.


If positive vs. non-negative matters:

section .rodata

word_array dw -1,2,0,-3,4
len  equ $-word_array     ; length in bytes.  assembler constant, so we can mov reg, imm8/imm32   rather than loading it as data.

section .text
proc_pos_and_neg:
  mov   esi, word_array ; esi points to the array.  In MASM, use OFFSET word_array
  xor   edx, edx           ; pos_count = 0
  xor   eax, eax           ; neg_count = 0
  mov   ebp, -1            ; constant to test with

  lea   edi, [esi + len]  ; points one past the end of the array
  xor   ebx, ebx          ; clear upper portion, because setcc r32 isn't available, only setcc r8  :(

countloop:
  test   bp, word [esi]   ; test reg, mem can micro-fuse, but test mem, imm can't
  setg   bl               ; 0 or 1, depending on  array[i] > 0
  lea    edx, [edx + ebx]  ; add without affecting flags
  setl   bl
  add    eax, ebx          ; can clobber flags now

  add    esi, 2            ; extra loop overhead compared to the scan-backwards decrementing an index reg I used last time
  cmp    esi, edi
  jb  countloop            ; loop while our pointer is below the pointer to one-past-the-end

ret     ; neg_count in eax,  pos_count in edx

I used a different loop structure here just for variety. The loop should be 7 uops, but on Intel pre-Haswell, reading ebx after setcc writes bl will incur a partial-register penalty (an extra uop to merge the result into the full reg).




回答2:


Read the comments

proc:
  mov si, data ; si points to the data
  mov cx, [len] ; cx gets the length of the data
  shr cx,1 ; the length was in bytes, we want words
  mov bx, 0
  mov dx, cx

checkNext:
  mov ax, [si]
  text ax, ax ; alternatively: test ax, 8000h
  js isNegative
  inc bx ; counting positive numbers

isNegative:
  add si, 2 ; moving to next word
  loop checkNext ; decrease cx, jump if not 0

  sub dx, bx ; bx has the positive numbers, dx - the negative ones
  ret ; done

data dw -1,2,-3,4
len dw $-data


来源:https://stackoverflow.com/questions/33685828/determine-number-of-negative-and-positive-numbers-in-an-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!