The MASM Forum

General => The Campus => Topic started by: JK on June 16, 2022, 09:32:48 PM

Title: Comparing a qword (unsigned) to real8
Post by: JK on June 16, 2022, 09:32:48 PM
The FPU treats integer numbers signed by default. In case of a DWORD i can have it unsigned by zero extending it to a QWORD and then loading it into the FPU. The largest integer number, which can be loaded in the FPU is a QWORD, so this extension trick won´t work for a QWORD and the FPU.

This makes it impossible to compare an unsigned QWORD to a float inside the FPU in general, if i didn´t miss something. So how else could i do it?


Thanks

JK
Title: Re: Comparing a qword (unsigned) to real8
Post by: jj2007 on June 16, 2022, 09:41:47 PM
Check the sign bits of both operands in memory, then see which one is "bigger".
Title: Re: Comparing a qword (unsigned) to real8
Post by: HSE on June 16, 2022, 10:29:21 PM
 :biggrin: I forget to read title.

You can take sign bit and load number without that bit, then add sign_bit*2^63.
Title: Re: Comparing a qword (unsigned) to real8
Post by: raymond on June 17, 2022, 03:43:21 AM
Let's make one thing clear related to the title of this thread.

A REAL8 float only has a maximum accuracy of 53 significant bits. Thus, ANY dword having an absolute value exceeding 53 bits cannot be compared with full accuracy to a REAL8, almost regardless of any trick you may want to devise.

Secondly, zero extending a (unsigned) dword to a qword and loading it to the FPU requires the FPU to be in REAL10 mode. Comparing that accurate number to a REAL8 number in REAL10 mode
a- would NOT improve the accuracy of that REAL8 number,
b- would not be making a comparison in REAL8 mode.
Title: Re: Comparing a qword (unsigned) to real8
Post by: JK on June 17, 2022, 08:45:39 AM
Maybe i should have written more general "floating point values" instead of "real8"!

It´s not about some trick for gaining accuracy, which i know is impossible. It´s about comparing unsigned integer numbers (qword beeing the largest) to floating point values. But i see your point now - such comparisons can get "problematic" because of accuracy.

The highest decimal value an unsigned qword can represent is 18.446.744.073.709.551.615 (2^64 - 1). This requires 20 decimal digits. A real4 has a mximum of 6 digits of precision, a real8 has 15 to 16 and a real10 has 18 to 19 digits of decimal precision.

So an exact comparison is not always possible, the best you can get sometimes is comparing "ranges" depending on the type of floating point and it´s accuracy. In other words the "range of inaccuracy" comparing an unsigned qword and a real10 could be up to be 2 decimal digits. We might get inaccurate results for qword values beyond 18 digits.

The only solution i see at the moment would be dividing both values by 100 (cut off 2 digits of precision, accept a granularity of 100) before loading and/or comparing an unsigned qword and a real10, if the qword needs more than 18 digits. Maybe dividing by 10 would be enough, i will test this ...


Thanks

JK
Title: Re: Comparing a qword (unsigned) to real8
Post by: HSE on June 17, 2022, 08:54:28 AM
JK,

In that way, you only have to divide by 2.( "shr reg, 1" for uqword).

HSE
Title: Re: Comparing a qword (unsigned) to real8
Post by: jj2007 on June 17, 2022, 06:05:29 PM
Sorry to come back to MasmBasic, but ArraySort does Qword comparisons all the time, and fast. It's not rocket science. Do a lab search on ArraySort. I am on a dumbphone, can't help you more.
Title: Re: Comparing a qword (unsigned) to real8
Post by: JK on June 17, 2022, 11:24:52 PM
@jj
comparing qwords to qwords or real8 to real8 is easy and straightforeward, the trouble begins, when you want to compare mixed types.

@all:
Having run some tests all i can say by now is: Comparisons between a signed qword and a real10 always yield a correct result. A signed qword has a maximum of 19 decimal places, which are fully covered by a real10. An unsigned qword needs a maximum of 20 decimal places, which doesn´t fit into the FPU.

And as a rule of thumb it is safe to compare word sized integers (signed and unsigned) to real4, dword sized integers (signed and unsigned) to real8. When trying to compare e.g a dword to a real4, you must consider that a dword with more than 6 decimal places cannot be exactly represented by a real4. Of course you will always get a result, but it might not be, what you expect.

It seems i must accept an inaccuracy in the last decimal place, when an unsigned qword reaches and exceeds 2^63. This is for comparing it to a real10, i must accept even more inaccuracy for real8 and real4.

I´m going to try, what HSE proposes ...


JK
Title: Re: Comparing a qword (unsigned) to real8
Post by: jj2007 on June 18, 2022, 02:59:08 AM
Quote from: JK on June 17, 2022, 11:24:52 PM
@jj
comparing qwords to qwords or real8 to real8 is easy and straightforeward, the trouble begins, when you want to compare mixed types

Oops, my fault. So it's apples vs oranges. No problem, once your real4 apple and your qword orange are on the fpu, they are both real10 fruit and can easily be compared using ucomisd or similar :thumbsup:
Title: Re: Comparing a qword (unsigned) to real8
Post by: raymond on June 18, 2022, 03:10:05 AM
Quote from: jj2007 on June 18, 2022, 02:59:08 AM
Quote from: JK on June 17, 2022, 11:24:52 PM
@jj
comparing qwords to qwords or real8 to real8 is easy and straightforeward, the trouble begins, when you want to compare mixed types

Oops, my fault. So it's apples vs oranges. No problem, once your real4 apple and your qword orange are on the fpu, they are both real10 fruit and can easily be compared using ucomisd or similar :thumbsup:

They may both be "fruits" BUT one is peeled and the other is not, even when both on the FPU. :thdn:
Title: Re: Comparing a qword (unsigned) to real8
Post by: jj2007 on June 18, 2022, 09:05:30 AM
I like how you spin the analogy further, Raymond ;-)
Title: Re: Comparing a qword (unsigned) to real8
Post by: HSE on June 18, 2022, 11:45:33 PM
Quote from: raymond on June 18, 2022, 03:10:05 AM
They may both be "fruits" BUT one is peeled and the other is not, even when both on the FPU. :thdn:

Yes  :thumbsup: There is something wrong in program design.
Title: Re: Comparing a qword (unsigned) to real8
Post by: jj2007 on June 19, 2022, 02:28:38 AM
Fact is that when you load a real4 of 1234567890123456.001 and compare it a qword 1234567890123456, the fpu will make a heroic effort to make you happy
Title: Re: Comparing a qword (unsigned) to real8
Post by: raymond on June 19, 2022, 02:51:17 AM
As a follow up, the fact is also that if you load a real4 of 1234567890123456 and compare it to a qword 1234567890123456, the fpu will then definitely NOT make you happy. :sad:
Title: Re: Comparing a qword (unsigned) to real8
Post by: jj2007 on June 19, 2022, 06:45:37 AM
The fpu will make you unhappy, but the answer will be correct: there is NO real4 1234567890123456.
Title: Re: Comparing a qword (unsigned) to real8
Post by: daydreamer on June 19, 2022, 09:26:11 AM
Quote from: raymond on June 19, 2022, 02:51:17 AM
As a follow up, the fact is also that if you load a real4 of 1234567890123456 and compare it to a qword 1234567890123456, the fpu will then definitely NOT make you happy. :sad:
The gui editor warns showing red digits when any type has too many digits
Read my sigline  :greenclp:
Title: Re: Comparing a qword (unsigned) to real8
Post by: hutch-- on June 20, 2022, 05:00:05 AM
This is a comment from a maths illiterate, if you have an integer in a QWORD and the same number in a 64 bit float (REAL 8) then it is only a conversion between comparing the two. 12345678901234 can be directly compared with 12345678901234.0  . The only risk is if the REAL 8 also has a fraction so that if you converted the bare integer to a REAL 8 and a REAL 8 number with a fraction, you are not comparing the same thing.
Title: Re: Comparing a qword (unsigned) to real8
Post by: raymond on June 20, 2022, 07:37:33 AM
QuoteThe only risk is if the REAL 8 also has a fraction .....

Hutch

The main risk is for people, without sufficient knowledge of the fpu, to think that QWORDS being 64-bit integers can be compared directly to 64-bit floats because they have the same memory foot print. The fact is that those REAL 8 floats have a maximum accuracy of only 54 bits.

Then, if the QWORD is larger than 54 bits, it would be almost impossible to determine if it is exactly equal to any 64-bit float, regardless if the latter is a fractional number or not. However, because floats can represent numbers much larger than the integer range, it remains possible to determine if a 64-bit float is greater or smaller than any QWORD (which may be the eventual result if both happen to be equal).
Title: Re: Comparing a qword (unsigned) to real8
Post by: Gunther on June 20, 2022, 02:45:46 PM
Raymond,

Quote from: raymond on June 20, 2022, 07:37:33 AM
The fact is that those REAL 8 floats have a maximum accuracy of only 54 bits.

that is absolutely correct. Strictly speaking, there are only 53 significant bits. This makes the comparison with a QWORD complicated.
Title: Re: Comparing a qword (unsigned) to real8
Post by: jj2007 on June 21, 2022, 08:56:14 AM
It is so simple:

MyQ dq 1234567890123456789
  ..
  fld FP4(1234567890123456789.0)
  fild MyQ
  deb 4, "The difference", ST(0), ST(1)

The difference
ST(0)           1234567890123456789.
ST(1)           1234567939550609408.


I really don't know what there is to discuss :cool:
Title: Re: Comparing a qword (unsigned) to real8
Post by: hutch-- on June 21, 2022, 10:20:43 AM
I wonder what the result would be if you use the SSE2 SD instructions ? I would imagine that the only difference would be the fraction.
Title: Re: Comparing a qword (unsigned) to real8
Post by: jj2007 on June 21, 2022, 10:42:17 AM
The point is you can't "pump up" a REAL4 or REAL8 to the 19 digits precision of a QWORD. It's simply not possible. Only a REAL10 has a 64-bit alias "QWORD" mantissa:

  fld FP4(1234567890123456789.0)
  fld FP8(1234567890123456789.0)
  fld FP10(1234567890123456789.0)
  fild MyQ
  deb 4, "The difference", ST(0), ST(1), ST(2), ST(3)

The difference
ST(0)           1234567890123456789.
ST(1)           1234567890123456789.
ST(2)           1234567890123456768.
ST(3)           1234567939550609408.
Title: Re: Comparing a qword (unsigned) to real8
Post by: Gunther on June 21, 2022, 12:32:23 PM
Quote from: jj2007 on June 21, 2022, 08:56:14 AM
I really don't know what there is to discuss :cool:

I know it. If you read the thread carefully (http://masm32.com/board/index.php?topic=10141.msg110687#msg110687), you quickly realize who is discussing and firing smoke candles here.

Quote from: jj2007 on June 21, 2022, 10:42:17 AM
The point is you can't "pump up" a REAL4 or REAL8 to the 19 digits precision of a QWORD. It's simply not possible.

Oh, really?

Quote from: jj2007 on June 21, 2022, 10:42:17 AM
Only a REAL10 has a 64-bit alias "QWORD" mantissa:

The title of the thread is: Comparing a qword (unsigned) to real8. And by the way: The mantissa of a REAL10 value is usually something different than a
QWORD - may it be signed or unsigned.
Title: Re: Comparing a qword (unsigned) to real8
Post by: JK on June 22, 2022, 10:14:27 PM
Finally i will stick to this solution:

  mov ecx,FFFFFFFD                       
  mov ebx,FFFFFFFF                       

  mov dword ptr ss:[ebp-40],ecx                       ;save qword in ecx/ebx to memory (could be eax/edx as well)
  mov dword ptr ss:[ebp-3C],ebx           
  fld st(0),tword ptr ds:[3C5848]                     ;load real10
  mov edx,dword ptr ss:[ebp-3C]           
  test edx,edx                           

  jns testme.3C1A50                                   ;skip, if sign bit of qword is not set
  shr dword ptr ss:[ebp-3C],1                         ; /2
  rcr dword ptr ss:[ebp-40],1             
  fild st(0),qword ptr ds:[3C58D4]                    ;2 (const)
  fdivp st(1),st(0)                                   ; /2

testme.3C1A50
  fild st(0),qword ptr ss:[ebp-40]                    ;load qword
  fcompp                                 
  fwait                                   
  fnstsw ax                               
  sahf                                   
  jne testme.3C1A9D                                   ;skip, if not equal
 
  ...
 
testme.3C1A9D 


this compares an unsigned qword to a real10 and these are some of the results:

Quote

18446744073709551614 = 18446744073709551614.0         -> ok
18446744073709551613 = 18446744073709551614.0         -> not ok
18446744073709551615 = 18446744073709551614.0         -> ok (wrong !!!)
18446744073709551614 = 18446744073709551613.0         -> not ok
18446744073709551614 = 18446744073709551615.0         -> not ok
18446744073709551613 = 18446744073709551613.0         -> not ok (wrong !!!)
18446744073709551613 = 18446744073709551614.0         -> not ok
18446744073709551613 = 18446744073709551615.0         -> not ok
18446744073709551613 = 18446744073709551612.0         -> ok (wrong !!!)

The pattern is obvious, these comparisons don´t always yield a correct result, because a real10 can hold a maximum of 19 valid decimal digits, so you might be off by 1. This gets even worse when comparing an unsigned qword to a real8 or real4. So you better don´t rely on results of such comparisons, unless you know about and understand the limitations.

Thanks for your input

JK 
Title: Re: Comparing a qword (unsigned) to real8
Post by: jj2007 on June 23, 2022, 12:22:53 AM
Here is an alternative way to deal with unsigned QWORDs (source attached, pure Masm32):

MyR10 REAL10 18446744073709551612.0 ; 2^64-4
MyQW QWORD 18446744073709551608 ; 2^64-8
...
  .Repeat
inc byte ptr MyQW
fild MyQW
.if !bl
fld FP10(18446744073709551616.0) ; add 2^64
fadd
.endif
fcomip ST, ST(1)
.if Zero?
print "equal", 13, 10
.elseif !Carry?
print "Qword bigger than Real10", 13, 10
.else
print "Qword smaller than Real10", 13, 10
.endif
inc esi
  .Until esi>=7


Qword smaller than Real10
Qword smaller than Real10
Qword smaller than Real10
equal
Qword bigger than Real10
Qword bigger than Real10
Qword bigger than Real10
Title: Re: Comparing a qword (unsigned) to real8
Post by: JK on June 23, 2022, 02:40:41 AM
Thanks jj,

i must admit, i currently don´t understand, why your sample works! Playing a bit with it, i made the following changes to get the sign bit for each and every real10


...
  mov ebx, 1
  finit            ; important: set 64-bit precision
  fld MyR10
  ftst
  fstsw ax
  sahf
  jb @F

  xor ebx, ebx
@@:
;  movzx ebx, byte ptr MyR10[9]
;  and bl, 1            ; get the sign bit
...


It works for the numbers you gave, but it fails for e.g these numbers:

QuoteMyR10    REAL10 9223372036854775808.0    ; 2^63
MyQW    QWORD 9223372036854775806    ; 2^63-2
:

So the basic idea seems to work, but there is something missing (or i did something wrong)
Title: Re: Comparing a qword (unsigned) to real8
Post by: jj2007 on June 23, 2022, 04:15:54 AM
You are right, something is seriously wrong. Inter alia, the test for the sign bit should be inside the loop. Asap I'll investigate.
Title: Re: Comparing a qword (unsigned) to real8
Post by: JK on June 23, 2022, 06:07:31 AM
jj, i think i got it:

  finit            ; important: set 64-bit precision
  fld MyR10

  xor esi, esi
  .Repeat
    add dword ptr MyQW, 1
    adc dword ptr MyQW[4], 0
    fild MyQW

  ftst
  fstsw ax
  sahf
  jae @F

  fld my64    ;FP10(18446744073709551616.0)    ; add 2^64
  fadd

@@:
    fcomip ST, ST(1)


you must not act on the sign bit of the float, you act on the sign bit of the qword. The above code passes all my tests so far ...
Which leaves the question, how exactly does it work, what does adding 2^64 do bitwise, so that the comparison yields a correct result - something like a two´s complement insde the FPU ?

JK
Title: Re: Comparing a qword (unsigned) to real8
Post by: raymond on June 23, 2022, 06:44:24 AM
Just my "2-bits" :skrewy: if it may help understand some of the peculiarities of the fpu now or in the future:

-Whenever you load an integer onto the fpu, it automatically ALWAYS considers it as a SIGNED integer. Trying to load the qword 264-1 (i.e. FFFFFFFFFFFFFFFF), the fpu would consider it as the value of -1.

However, because the fpu has a 64-bit mantissa in REAL10 mode, and the sign bit is defined separately, it could represent the 264-1 value as a negative number even though that cannot be achieved as a 64-bit negative integer.
Title: Re: Comparing a qword (unsigned) to real8
Post by: jj2007 on June 23, 2022, 06:52:46 AM
Quote from: JK on June 23, 2022, 06:07:31 AM
    add dword ptr MyQW, 1
    adc dword ptr MyQW[4], 0

Excellent :thumbsup:

Quoteyou must not act on the sign bit of the float, you act on the sign bit of the qword

Indeed, that's what I meant with "the test for the sign bit should be inside the loop".

Quotewhat does adding 2^64 do bitwise

As Raymond also showed, it simply turns a number like -3 into its unsigned equivalent. And that's what you want, right?

Simplified version attached (sahf & friends are awfully slow).
Title: Re: Comparing a qword (unsigned) to real8
Post by: daydreamer on June 23, 2022, 09:34:44 PM
Quote from: raymond on June 23, 2022, 06:44:24 AM
-Whenever you load an integer onto the fpu, it automatically ALWAYS considers it as a SIGNED integer. Trying to load the qword 264-1 (i.e. FFFFFFFFFFFFFFFF), the fpu would consider it as the value of -1.
so in .data section use signed versions of WORD DWORD etc SWORD SDWORD is useless,but is it only works for .IF macro comparisions ?
Title: Re: Comparing a qword (unsigned) to real8
Post by: jj2007 on June 23, 2022, 10:20:37 PM
For the record, here the essentials of an optimised version:

MyR10 REAL10 9223372036854775808.0 ; 2^63
MyQW QWORD 9223372036854775806 ; 2^63-2
...
  fld MyR10
  xor esi, esi
  .Repeat
fild MyQW
test byte ptr MyQW[7], 128
.if !Zero?
fadd FP4(18446744073709551616.0)  ; translate a negative number to its unsigned equivalent
.endif
fcomi ST, ST(1) ; could be fcomip, but we want to print the value
.if Zero?
PrintLine "equal"
.elseif Carry?
PrintLine Str$("ST=%i is smaller than ST(1)", ST(0))
.else
PrintLine Str$("ST=%i is bigger than ST(1)", ST(0))
.endif
fstp st
add dword ptr MyQW, 1
adc dword ptr MyQW[4], 0
inc esi
  .Until esi>=5


Output:
ST=9223372036854775806 is smaller than ST(1)
ST=9223372036854775807 is smaller than ST(1)
equal
ST=9223372036854775809 is bigger than ST(1)
ST=9223372036854775810 is bigger than ST(1)