News:

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

Test sign of two FP-numbers

Started by Rsir, July 30, 2009, 09:52:43 PM

Previous topic - Next topic

Rsir

Hi,
May I ask to have a glance over my actual puzzle.

Translating a prg that i earlier wrote in C and now i can't further.

prg branches if two FP numbers (V0 and V2) are both positive, both negative or both zero.
In C:
While (1)
  {
.
.
IF (V0==0 && V2==0) BREAK;
IF (V0> 0 && V2> 0) BREAK;
IF (V0< 0 && V2< 0) BREAK;
.
.
  }


In MASM32
;;  .IF (V0==0) && (V2==0)
;     .BREAK
;  .ENDIF
;  .IF (V0> 0) && (V2> 0)
;     .BREAK
;  .ENDIF
;  .IF (V0< 0) && (V2< 0)
;     .BREAK
;  .ENDIF


Won't work
Probable because the variables are declared as floating point numbers.

After reading the excelent doc of Raymond Filiatreault
i stuck at:
Quote; FLD   V0
; ftst              ;compare the value of ST(0) to +0.0
; fstsw ax          ;copy the Status Word containing the result to AX
; fwait             ;insure the previous instruction is completed
; sahf              ;transfer the condition codes to the CPU's flag register
; jpe error_handler ;the comparison was indeterminate
;                   ;this condition should be verified first
;                   ;then only two of the next three conditional jumps
;                   ;should become necessary, in whatever order is preferred,
;                   ;the third jump being replaced by code to handle that case
; ja   st0_positive ;when all flags are 0
; jb   st0_negative ;only the C0 bit (CF flag) would be set if no error
; jz   st0_zero     ;only the C3 bit (ZF flag) would be set if no error
;

Maybe the best way is to convert the reals to integers, do the test, and go on?
But i hope there is a neat trick using FPU-coding.

Who wants to write me something about this?
Rsir

dedndave

all in all, it may be best to convert to integers, depending on the size of the number set and the application
in some cases, the fpu has too much resolution
two different numbers that are very close together will compare as not equal
in some cases, you may want them to return equality
by converting to integers, you can get away from that problem

as for your code, i assume you realize the semicolons at the beginning of each line makes them all comments ?

probably the best way to test is to get a 0.0 in st(0) and leave it there during the entire test
(it will be st(1) when you load the test values)
fld to get a number
compare st(0),st(1) (whichever compare instruction suits your needs)
pop the value off the fpu stack with the compare
push that status word on the cpu stack
now, load the other value
and do the same
when you are done, AND the two status words together, AND that with a mask to create a single value to control the branching
oh - and discard the 0 (ffree), unless it is good for something else


dedndave


        fld0
        fld     ValueA
        fcomip  st(0),st(1)
        fld     ValueB
        pushfd
        fcomip  st(0),st(1)
        fwait
        pushfd
        pop     eax
        pop     edx
        mov     ah,al
        xor     ah,1
        mov     dh,dl
        xor     dh,1
        and     ax,dx
        and     ax,19h

there - we needed CARRY and INVERSEofCARRY
if both values are equal to 0, bit 3 will be set
if both values are negative, bit 0 will be set
if both values are positive, bit 8 will be set
i think that's right (i may have bit 0 swapped with bit 8 in that logic - lol)
let me test it

Rsir

Thank you for your fast reaction dedndave.
The semicolons are there because the code doesn't work. And not the other way around.
The last suggestion in your first post will solve the puzzle i think.
And your second post will speed up the process.
I'm going to work on that.
thanks & regards,
Rsir

drizz

#4
OTOMH,

REAL4
mov eax,V0
mov edx,V1
mov ebx,eax
mov ecx,edx
and ebx,7FFFFFFFh
and ecx,7FFFFFFFh
and eax,80000000h
and edx,80000000h
or ebx,ecx
jz both_are_zero
cmp eax,edx
jz both_are_gt_0_or_lt_0

REAL8
mov eax,dword ptr V0+4
mov edx,dword ptr V1+4
mov ebx,eax
mov ecx,edx
and ebx,7FFFFFFFh
and ecx,7FFFFFFFh
and eax,80000000h
and edx,80000000h
or ebx,ecx
or ebx,dword ptr V0
or ebx,dword ptr V1
.break .if zero? || eax==edx


edit: added real8 test
The truth cannot be learned ... it can only be recognized.

dedndave

hiyas drizz
i was trying to find a solution for all sizes of reals
also, there can be +0 and -0
at the moment, i am a little confused by the result i am getting here

include \masm32\include\masm32rt.inc
.686

.data

Zero10 real10 0.0
ValueA real10 0.0
ValueB real10 0.0

.code

start:  finit
        fld real10 ptr Zero10
        fld real10 ptr ValueA
        fcomip  st(0),st(1)
        fwait
        pushfd
        fld real10 ptr ValueB
        fcomip  st(0),st(1)
        fwait
        pushfd
        ffree   st(0)
        pop     eax
        pop     edx
print uhex$(eax),13,10
print uhex$(edx),13,10
exit
        end     start

output:

00000242
7C90EB94
  :dazzled:
EDIT DOH ! - lol
forgot that print modifies edx - lol

dedndave

this one works with any size reals
if both values = 0, al = 1
if both values > 0, al = 2
if both values < 0, al = 4
otherwise, al = 0

include \masm32\include\masm32rt.inc
.686

.const

Zero10 real10 0.0

.data

ValueA real10 0.0
ValueB real10 0.0

.code

start:  finit
        fld real10 ptr Zero10
        fld real10 ptr ValueA
        fcomip  st(0),st(1)
        fwait
        pushfd
        fld real10 ptr ValueB
        fcomip  st(0),st(1)
        fwait
        pushfd
        ffree   st(0)
        pop     eax
        pop     edx

        mov     ecx,eax
        or      ecx,edx
        shl     ecx,2
        shr     cl,2
        or      cl,ch
        xor     cl,1

        and     eax,41h
        and     eax,edx
        shr     ecx,1
        rcl     eax,1
        rol     al,1

        print   uhex$(eax),13,10

        exit

        end     start

raymond

QuoteIF (V0==0 && V2==0) BREAK;
   IF (V0> 0 && V2> 0) BREAK;
   IF (V0< 0 && V2< 0) BREAK;

If you call them "floats", they are probably in the single precision 32-bit floating point format. In that case, I would use the following:

   mov  eax,V0
   xor  eax,V2
   jz   BREAK    ;both values would be equal, whether 0 or not
                 ;thus necessarily have same sign
   .if  V0 != 0 && V2 != 0   ;no break if either value = 0
      shl  eax,1    ;shift sign bit to CARRY flag
      jnc  BREAK    ;the sign bit would have been the same
   .endif
;code for values having different signs


The only case not covered would be when one value = -0 but is such a remote possibility that it can be disregarded.

The above could be adapted to 64-bit and 80-bit floats by doing the same but with only the most significant 32 bits. There again, the -0 case is not covered in addition to comparing them with denormalized floats (also a very remote possibility).
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

dedndave

you must be missing something, Ray - or i am - lol
there are 9 cases - 3 of which return a branching result
the other 6 cases branch to the same location
of the 9 cases, the likelyhood of the 2 values being equal to each other seems remote, and is not one of the cases sought
EDIT - i think i get it
maybe i was making it harder than it had to be - lol
you can see from my result that i thought it had to branch seperately
Quoteif both values = 0, al = 1
if both values > 0, al = 2
if both values < 0, al = 4
otherwise, al = 0

dedndave

if you test for zero before testing for sign, you can accomodate -0

        mov     eax,V1
        shl     eax,1
        jnz     test_for_sign

        mov     eax,V2
        shl     eax,1
        jnz     CONTINUE

        jmp short BREAK

test_for_sign:
        rcr     eax,1
        xor     eax,V2
        shl     eax,1
        jnc     BREAK

CONTINUE:

        jmp short DONE

BREAK:

DONE:

ToutEnMasm


To know the sign of a real number , there is just to refer to the IEEE 754 format.
For 32 bits number,bit 31 is the signe
For 64 bits number , bit 63 is the sign.
If the bit is one,the number is negative,else positive.

raymond

Quote        mov    eax,V1
        shl     eax,1
        jnz     test_for_sign

That does not cover the case where V2=0.

If checking for -0 is an absolute necessity, then
   mov  eax,V1
   shl  eax,1
   .if ZERO?
      mov  eax,V2
      shl  eax,1
      jz   BREAK     ;both 0
      jmp  CONTINUE  ;only one 0
   .else
      mov  eax,V2
      shl  eax,1
      jz   CONTINUE  ;only one 0
   .endif
;neither is 0
   mov  eax,V1
   xor  eax,V2
   shl  eax,1
   jnc  BREAK

CONTINUE:

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

dedndave

oh - i see
you caught me Ray (again - lol)

dedndave

this one works - i was trying to get it down to one branch
it is do-able, but it takes too much code

        mov     eax,V1
        shl     eax,1
        lahf
        mov     edx,V2
        mov     al,ah             ;bit 6 = zf1, bit 0 = sign1
        shl     edx,1
        lahf                      ;bit 14 = zf2, bit 8 = sign2
        xor     al,ah             ;bit 14 = zf2, bit 6 = (zf1 <> zf2), bit 0 = (sign1 <> sign2)
        and     ax,4041h
        jz      BREAK             ;both>0 or both<0

        xor     ah,40h
        shr     ax,1
        jz      BREAK             ;both=+/-0

raymond

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