News:

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

Comparing real variables: fcmp

Started by jj2007, December 28, 2009, 09:51:59 PM

Previous topic - Next topic

jj2007

Quote from: qWord on December 30, 2009, 02:03:52 AM
a quote from AMD's developers Manuals:
QuoteException Status (ES). Bit 7. The processor calculates the value of this bit at each instruction
boundary and sets the bit to 1 when one or more unmasked floating-point exceptions occur...
By default, the OE is masked  (in control register) -> bit 7 will not set.
Thanks, that is crystal clear now.

Quote from: qWord on December 30, 2009, 02:03:52 AM
Quote from: jj2007 on December 30, 2009, 12:04:39 AM
jpe error_handler ;the comparison was indeterminate[/b] after sahf - this does not seem to work.
this only applies, if you are comparing values - if the operants (if one or both depends on instruction) are not compareable (e.g. NaN's) the flag C2 is set. C2 becomes to the Parity Flag, when setting AH to the rFLAGS.
I could not produce this condition, but if it was present, an exception bit would be set, too (correct me if I am wrong). So in the end I opted for testing the whole lowbyte, see below.

Quote
Here a two good references( primary literature  :green2) for fpu-stuff (IMO):
AMD64 Architecture Programmer's Manual Volume 1: Application Programming (chapter 6)
AMD64 Architecture Programmer's Manual Volume 5: 64-Bit Media and x87 Floating-Point Instructions
Thanks :thumbu

Quote from: MichaelW on December 30, 2009, 07:58:27 AM
Quotebut it seems to require a P6. Some members here still use a P3
fcomi and fcomip work fine on a Pentium III. P6 normally refers to the sixth generation x86 processors that started with the Pentium Pro.
http://en.wikipedia.org/wiki/Intel_P6
Thanks for clarifying. In the end, I had to test the status word anyway, so fcomi is not needed.

QuoteAlso, for EQ, GT, LT, etc you should be able to compare the values as integers and interpret the flags just as you would for integers.
I thought that is exactly what the macro is doing... can you give an example, please?

Below the code as needed for pasting to the top of an application. On my Celeron, fcmp takes 37 cycles.

fcmp MACRO cmp1:REQ, cmp2 ; -------- float comparison --------
LOCAL oa
  ffree st(7)
  ifb <cmp2>
fldz ; no second arg; compare against zero
  else
oa = (opattr cmp2) AND 127
if (oa eq 36) or (oa eq 48)
push cmp2
fild dword ptr [esp] ; integer or reg32 on stack, then on FPU
pop eax
else
fld cmp2 ; real on FPU
endif
  endif
  ffree st(7)
  fld cmp1
  call fcmpP ; fcmpP must be in code section
ENDM ; --------- end of fcmp macro --------
.code
fcmpP_proc:
Comment @
Typical call with macro:
fcmp MyReal4, MyReal8
.if Zero?
MsgBox 0, "Equal", "fcmp:", MB_OK
.elseif Sign?
MsgBox 0, "Below", "fcmp:", MB_OK
.else
MsgBox 0, "Above", "fcmp:", MB_OK
.endif
Typical call by hand:
  ffree st(7) ; free a slot on the FPU
  fldz ; compare against zero
  ffree st(7)
  fld MyReal4 ; load the value to compare with
  call fcmpP
  jxx MyLabel ; branch as needed @

fcmpP proc
push edx
xor edx, edx ; clear the flag register
fcompp ; compare ST(0) with ST(1) and pop twice
fstsw ax ; move FPU flags C1 etc to ax
test al, -1 ; test if any (e.g. overflow or precision) flags are set
je @F ; flags set=error
sub edx, 127 ; produce error code
fclex ; clear exceptions
@@: sahf ; translate flags (jpe... not needed, covered by test al, -1)
ja fcPos
je @F
dec edx ; negative (-2+1=-1)
@@: dec edx ; or zero (0-1+1=0)
fcPos: inc edx ; positive
xchg edx, [esp] ; save edx, use eax as retval
pop eax
ret
fcmpP endp
fcmpP_endp:
.const ; more macros etc below

MichaelW

Quotei think if you weed out special values like NaN's, infinities, negative 0's, etc, that is correct
but comparing reals depends on the application

If the application is to compare two floating-point numbers to determine their relationship, that can be done by reinterpreting them as integers. For a simple example:

f1 real4 12.34
f2 real4 12.33
;f1 = 01000001010001010111000010100100b = 1095069860 (as integer)
;f2 = 01000001010001010100011110101110b = 1095059374 (as integer)
mov eax, dword ptr f1
cmp eax, dword ptr f2
jxx somewhere


http://www.cygnus-software.com/papers/comparingfloats/Comparing%20floating%20point%20numbers.htm
eschew obfuscation

jj2007

Michael, Dave,

Dawson's article is indeed worth reading. It goes well beyond my suggestion above to save the two numbers as REAL4, and then check if the integers are identical, i.e. the floats are "equal" within the limits of REAL4 precision.

Integer comparison is probably a lot faster. It becomes tricky, however, when dealing with different sizes. The fcmp macro doesn't care whether arg2 is eax, an integer, a REAL4 or REAL10, because it shoves it up the FPU anyway and uses fcompp there. If you want to achieve the same with integer comparison à la Dawson, you need to convert the non-matching args to a matching format - which might turn out to be expensive. And you need error handling that fcmp does through the FPU's flags.