15
\$\begingroup\$

I have implemented an algorithm which converts a signed integer to its ASCII equivalent string with the corresponding sign as a first byte. I have tested it on several numbers and it worked correctly. But as a newbie I might have not noticed some logic mistakes or flaws.

I am asking for any ideas how to make this more efficient and elegant.

;;;;MACRO SECTION//////////////////////////////////////////////////////////////
%macro Int2ascii 2 ;1 - number, 2 - string
 mov r12, %2 ;move ADDRESS of string
 mov eax, dword[%1]
 cmp eax, 0
 jge %%placePlus ;check either number is negative or positive
 %%placeMinus:
 mov byte [r12], "-" ;fill the first (0th) byte with the sign
 mov rdi, -1 ;remember the sign
 ;neg eax ;negate number and forgot the sign (deprecated: see EDIT 1)
 jmp %%skipPlacePlus 
 %%placePlus:
 mov byte [r12], "+"
 mov rdi, 1 ;remember the sign
 %%skipPlacePlus:
 xor rcx, rcx ;digit counter here (used implicitely in %%fillLoop)
 mov ebx, 10 
 %%divideLoop:
 cdq ;eax -> edx:eax
 idiv ebx ;signed division, reminder is also signed
 imul rdx, rdi ;"unsign" the current reminder
 ;div ebx ;(deprecated: see EDIT 1)
 push rdx ;push the reminder to stack
 inc rcx ;increment digit counter
 cmp eax, 0
 jne %%divideLoop
 mov rdi, 1 ;note that the first (0th) byte was number sign (+/-) 
 %%fillLoop:
 pop rax ;pop back reminders
 add al, "0" ;ascii code for digit
 mov byte [r12 + rdi], al ;fill string 
 inc rdi 
 loop %%fillLoop 
 mov byte [r12 + rdi], 0x00 ;terminate string
%endmacro

Example of output

As @TobySpeight suggested I have tested the above algorithm on numbers: -1, 0, 1, 9, 10, 0x7fffffff, 0x80000000. Output is (gdb session):

(gdb) x/s &sNegOne
0x600377: "-1"
(gdb) x/s &sZero
0x600368: "+0"
(gdb) x/s &sPosOne
0x600386: "+1"
(gdb) x/s &sNine
0x600395: "+9"
(gdb) x/s &sTen
0x6003a4: "+10"
(gdb) x/s &sNumber1
0x6003b3: "+2147483647"
(gdb) x/s &sNumber2
0x6003c2: "-2147483648"

I have realized that trying to consider only unsigned positive numbers leads to double-word overflow for initially valid double-word number 0x80000000 but invalid after taking absolute value 0x7fffffff + 1 which is no longer signed double-word number. I have overcome it by signed division. The problem here is for signed division reminders are also signed and no more applicable for storing as digits without additional processing which is just multiplying by -1 or 1 depending on sign.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Mar 15, 2018 at 16:32
\$\endgroup\$
5
  • \$\begingroup\$ Most newbies write less-readable assembly than you did. Nice job so far. \$\endgroup\$ Commented Mar 15, 2018 at 16:51
  • 2
    \$\begingroup\$ "I have tested it on several numbers" is a good start. Better would be "Here is a test suite that exercises all the boundary cases" - I recommend you demonstrate at least -1, 0, +1, 9, 10, 0x7fffffff, 0x80000000. \$\endgroup\$ Commented Mar 15, 2018 at 17:32
  • \$\begingroup\$ @TobySpeight, on all suggested numbers it works fine except, of course, ... the last one: 0x80000000. This is because my negation line (when I'm "forgotting" the sign). Double-word overflows there. I will be thinking how to overcome this problem. Thank you. \$\endgroup\$ Commented Mar 15, 2018 at 18:36
  • \$\begingroup\$ You don't want to use cdq. That sign extends EAX into EDX, but since you're treating EAX as unsigned at that point you just want to zero out EDX. \$\endgroup\$ Commented Mar 15, 2018 at 19:05
  • \$\begingroup\$ @1201ProgramAlarm, you are absolutely right, thank you. But see the EDIT1 section (coming soon). \$\endgroup\$ Commented Mar 15, 2018 at 19:09

1 Answer 1

1
\$\begingroup\$
  1. The trick to not counting -0x80000000 as a negative number is to use unsigned division. IDIV is signed, DIV is unsigned.

  2. One option to consider: If you fill the buffer from the end and output the pointer to the start of the data, then you don't need to relocate things. Admittedly, this makes it harder to use in some cases.

  3. You might be better using DI (instead of R12) to hold your output address, and STOSB to store a character there. It not only writes the character, but increments DI.

answered Mar 2, 2020 at 11:58
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.