Hey everybody!
Ok, first off, I'm new to this forum.
Second off, I read hiroshimator's sticky and I hope that this post
doesn't go against his principles for this forum (i.e., don't be like
"help me do this")
Third, the "beef" of my post:

I had read somewhere awhile ago that there is a way to do
absolute value without a jump. This is what I coded up:

absolute value of eax:
mov edx,eax
shl edx,1 ;or add edx,edx, or mul edx,2 ... jk :)
sbb edx,edx
add eax,edx
xor eax,edx

Is this good/bad? does someone have something better?
as far as I can tell, if eax is positive, it really doesn't do anything
except nuke edx. If negative, it'll do not (dec x) which is abs, right?

Anyways, see you all later :)

--Chorus
Posted on 2002-03-13 11:19:07 by chorus
For those with 686 & above, you can use this:



.686
mov edx, eax
neg edx
cmovns eax, edx


Basically use the conditional move instruction after a neg of your target number. As neg sets the sign flag, you know whether or not to move it based on that flag!


---- Edit ----
A variation on your code should be as fast as my code above, and work on all processors (only downside being it is slightly bigger):


mov edx,eax
; shl edx,1
; sbb edx,edx
sar edx, 31
add eax,edx
xor eax,edx


Mirno
Posted on 2002-03-13 11:57:52 by Mirno
absolute MACRO regmem:REQ

rcl regmem, 1 ; top bit
sbb eax, eax ; all bits like top bit
rcr regmem, 1 ; put it back
;nop
add regmem, eax
;nop
xor regmem, eax
ENDM
Posted on 2002-03-13 12:01:17 by bitRAKE
Hey! I like both versions :)
Mirno, the 686 version is pretty elegant, but to be honest
I don't know enough about the cmovcc instructions to say
if it's faster or just shorter. Does the instruction pair well?
I'll look it up I guess. Thanks for the idea :)

BitRake, I like the universality of the macro. I coded mine
assuming it's in eax, but that's where my values usually are.
I think I'll change my code. However, I might have to one up
you. I just thought of this:

(I'm gonna assume it's in eax again)

mov edx,eax
sar edx,31
add eax,edx
xor eax,edx

it's one less instruction, and I'm sure sar is fully pairable still

the macro would just go like so

mov eax,regmem
sar eax,31
add regmem,eax
xor regmem,eax

but won't it be faster to mov a mem into a reg, do all this stuff
and mov it back? i.e.,

mov edx,mem
mov eax,edx
sar eax,31
add edx,eax
xor edx,eax
mov mem,edx

or is it not worth it?

Anyways, that's my 2 bits :)

Thanks again

--chorus
Posted on 2002-03-13 12:25:02 by chorus
cmp regmem, 80000000h
cmc ; fixed *Thanks* Mirno
sbb eax, eax

add regmem, eax

xor regmem, eax

:grin:
Posted on 2002-03-13 13:02:28 by bitRAKE
Chorus, pairing is "old hat" :grin:
The Pentium architecture used two execution pipes, but the P6 core (the PPro/P2/PIII), uses a different method. Basically it breaks instructions down into RISC like components, and can execute several of these in one clock cycle (not only that it can do them in a different order to those of the instructions if there are no dependencies). As all the instructions involved are on registers, and are basic or arithmetic operations there should really be no delay in their execution.

Your original: 5 micro-ops (the RISC like components of instructions)
My variation: 4 micro-ops
My 686 variation: 4 micro-ops

As for running the code on the PPlain, the "SAR" version will pair better, as the shl and the sbb can both only execute on the u pipe.

bitRAKE:
subtracting 0 from any number will never set the carry flag (which is what sbb uses) :grin:
I think you need to subtract max_neg_int from it to set the carry flag (8000 0000h), and this will have the effect of setting the carry flag in the wrong case :( so you need an extra not, or complement the carry flag.

Mirno
Posted on 2002-03-13 13:52:17 by Mirno
Mirno, thanks for clearing that up - I don't have any documentation
here, and only enough time to give bad advise. :tongue:
Posted on 2002-03-13 14:04:29 by bitRAKE
Just out of curiosity... why would you want to do an abs() without a jump? ;)

Normally I would just do:

test eax, eax
jns @F
neg eax

Is there something inefficient about this method?
Posted on 2002-03-13 14:50:16 by iblis
iblis, branch miss-prediction is very costly on newer processors - forward branches aren't predicted on first time execution.
Posted on 2002-03-13 14:53:14 by bitRAKE
bitRAKE, your updated code will always give the negative value!
Add a neg at the end, or cmc between the comparision and the sbb.

Subtracting 80000000h from a number gives NOT(sign) in the carry flag!

Mirno
Posted on 2002-03-14 05:32:31 by Mirno
This is one solution:

cdq
add eax,edx
xor eax,edx
Posted on 2002-03-14 09:29:10 by gliptic
This seems to simple to be true -- so please explain what a negative number looks like:
and     eax, 0x7FFFFFFF
Posted on 2002-03-15 01:35:55 by eet_1024
eet, your method would work if the left most bit were merely a sign bit, it isn't though.

I will use 8bit to cut down on typeing, but this is the basic way binary numbers are stored:



bit: 7 6 5 4 3 2 1 0
value: -128, 64, 32, 16, 8, 4, 2, 1


Hence -1 = 1111 1111 (binary)

Anding it with 07Fh (0111 1111) will result in a value of 127.

The correct method to convert from signed to unsigned (and vice versa) is not, then add 1, or subtract 1, then not.

And to do this without a jump, you need to sign extend, add it (sign extend 1 (negative) gives -1, sign extend 0 (positive) gives zero. Add the -1 or zero, then xor with -1 or 0, xor with 0F...Fh gives the effect of a not, xor with 0...0h does nothing.
All the methods above did the same thing, and only varied in the way they get the sign extended register to add & xor with.

Mirno
Posted on 2002-03-15 04:27:01 by Mirno

This seems to simple to be true -- so please explain what a negative number looks like:
and     eax, 0x7FFFFFFF


This will not work with integer values, but it does work with floating point values.

like:



mov eax, [myFloat]
and eax, 07FFFFFFFh
mov [myFloat], eax


It does not work with integers because
-1 = FFFFFFFFh thus the result will be 07FFFFFFFh and not 1
Posted on 2002-03-15 10:08:00 by dxantos