News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Inverting -2147483648

Started by 00100b, January 11, 2005, 01:54:19 PM

Previous topic - Next topic

00100b

Still working on this "DWORD To ASCII String" conversion (to handle both signed and unsigned DWORDs), but something that I'm having difficulty with is the conversion of the lowest possible value for a signed-dword.  Each attempt results in the value being rounded to 214748365 (the invoked MessageBox displays '-214748365' when the value 80000000h is passed to the conversion procedure).

I've basically taken the code for the DWTOA procedure in the \MASM32\M32LIB directory and modified the sign: section of the code using the following attempts:


; First, the original block that used the NEG instruction.
; Two's complement.
sign:
    jns pos
    mov byte ptr [edi], '-'
    neg eax
    inc edi



; Second, the attempt to use the formula (I emphasize the word ATTEMPT):
;   zero minus negative-value = positive-value
sign:
    jns pos
    mov byte ptr [edi], '-'
    mov edx, 0
    mov ecx, eax
    sub edx, ecx
    mov eax, edx



; Third, the attempt to use the formula (again ATTEMPT):
;   negative-value multiplied by negative-one = positive-value
sign:
    jns pos
    mov byte ptr [edi], '-'
    mov edx, eax
    mov ecx, -1
    mul ecx
    mov eax, edx



; My last ATTEMPTs (two different methods for the same formula)
; One's complement plus one

; method 1
sign:
    jns pos
    mov byte ptr [edi], '-'
    not eax
    add eax, 1

; method 2
sign:
    jns pos
    mov byte ptr [edi], '-'
    not eax
    lea eax, [eax+1]


Can one of you fine humans place me back on the beaten path?

Thank you

Tedd

"neg eax" will make the positive number in eax into its negative equivalent.
Your problem might come indirectly from elsewhere(?)
(Stupid point, but make sure you do the negation after finding the rest of the value; sorry if you have, I haven't looked at the whole code)
No snowflake in an avalanche feels responsible.

00100b

Thanks for the response Tedd,

The code, using the DWTOA procedure contained in the MASM32.LIB file:

.586P
.MODEL FLAT STDCALL
OPTION CASEMAP :NONE

    INCLUDE c:\masm32\include\windows.inc
    INCLUDE c:\masm32\include\user32.inc
    INCLUDE c:\masm32\include\kernel32.inc
    INCLUDE c:\masm32\include\masm32.inc

    INCLUDELIB c:\masm32\lib\user32.lib
    INCLUDELIB c:\masm32\lib\kernel32.lib
    INCLUDELIB c:\masm32\lib\masm32.lib

.DATA?
    lpDebugMsg BYTE 200 DUP(?)
.DATA
    hInstance DWORD 0
.CODE
START:
    INVOKE GetModule, NULL
    MOV hInstance, EAX

    INVOKE dwtoa, -2147483648, ADDR lpDebugMsg
    INVOKE MessageBox, 0, ADDR lpDebugMsg, 0, 0

    INVOKE ExitProcess, 0
END START


Using the unmodified code for the DWTOA procedure in the MASM32.LIB file, that uses the NEG instruction, produces a message box that displays:

-214748365

This is what prompted me to go down the path of providing other methods for trying to obtain the desired results of a message box that displays:

-2147483648

The code that I used for my version of the DWTOA procedure alters nothing of the rest of the DWTOA procedure except the attempts to replace the:

NEG EAX

line of code with the lines outlined in my above post.

raymond

00100b

Forget that DWTOA procedure in the M32LIB. That procedure was developed to gain a few nanoseconds when compared to a standard conversion procedure. However, it is unfortunately defective for numbers in the high range and this has been well documented in other posts. (IMHO, that procedure should be removed from any future issue of the MASM32 package.)

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

00100b

Thanks for your response.

I would be interested in learning more about a "standard conversion procedure".  Could you provide me with direction as to where I might find one to study?

Thank you.

raymond

The "standard" procedure is to divide the dword (extended to a qword in EDX:EAX) by 10, storing each remainder (located in EDX), and continuing to do the same until EAX=0, and then reconstructing the ASCII string.

Optimizations are often aimed at eliminating the division which is one of the slowest instructions and replacing it with a multiplication using the reciprocal of 10 (which is also slow but much faster than a division). The main problem is that the reciprocal of 10 is not an exact binary number and could lead to occasional errors.

Here is a typical example of a "standard" procedure, starting with the dword located in EAX, and EDI pointing to a memory buffer.

   mov  ebx,eax     ;keep a copy of it
   mov  ecx,10
   push 0           ;to be used as a "stop sign"
   or   eax,eax     ;test for sign
   jns  @F
   neg  eax         ;make it positive
@@:
   xor  edx,edx     ;rezero EDX
   div  ecx
   add  dl,30h      ;convert to ASCII
   push edx         ;store temporarily on the stack
   or   eax,eax     ;test if conversion completed
   jnz  @B

   or   ebx,ebx     ;test if number was negative
   jns  @F
   mov  al,"-"
   stosb            ;insert "-" sign if negative
@@:
   pop  eax         ;retrieve stored digits
   stosb            ;transfer to memory buffer
   or   eax,eax     ;test if "stop sign" was reached
   jnz  @B


Your memory buffer now contains the null-terminated ASCII string. You will notice that the above code will properly return a null-terminated "0" string if EAX=0.

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

MichaelW

Forby,

The basic concept behind the "standard" procedure can be illustrated as:

1234 / 10 = 123 remainder 4
123 / 10 = 12 remainder 3
12 / 10 = 1 remainder 2
1 / 10 = 0 remainder 1

The digits values are extracted in reverse order, and the stack operations provide a convenient method of putting them in the correct order.

eschew obfuscation

00100b

Thanks for the response and the code-snippet.

I will be taking a good look at it (I have a habit of stepping through things on paper first).  This code snippet looks like the logic that I was starting to map out.  It will be interesting to see how my "attempt" compares.

Thank you.

00100b

Quote from: MichaelW on January 11, 2005, 05:45:07 PM
Forby,

The basic concept behind the "standard" procedure can be illustrated as:

1234 / 10 = 123 remainder 4
123 / 10 = 12 remainder 3
12 / 10 = 1 remainder 2
1 / 10 = 0 remainder 1

The digits values are extracted in reverse order, and the stack operations provide a convenient method of putting them in the correct order.


Thank you for the response.

You see, this was the opposite of what I was thinking.  I'm glad you posted this.

I was taking the approach of going from the billionths-place, and using the quotient (instead of starting at the right and moving left).  Then reducing the original value by the quotient * the divisor.  Divide the divisor by 10, wash, rinse and repeat.

I will work on a version using both approaches.  One thing that I just thought of, is that I wouldn't have to unroll the stack to populate the buffer.

Thanks again.