Author Topic: Large integers and floats  (Read 18073 times)

LiaoMi

  • Member
  • ****
  • Posts: 948
Re: Large integers and floats
« Reply #45 on: August 27, 2021, 08:53:24 PM »
Hi nidud,

thanks  :thup:, what about multiplication and division? Are these features available?

nidud

  • Member
  • *****
  • Posts: 2307
    • https://github.com/nidud/asmc
Re: Large integers and floats
« Reply #46 on: August 28, 2021, 02:52:37 AM »
The expression evaluator works up to 16 byte-sized values (unsigned, signed and floats). I've done some experiments using this for the operator directive but nothing is implemented yet.

JK

  • Member
  • **
  • Posts: 156
Re: Large integers and floats
« Reply #47 on: September 19, 2021, 06:00:25 PM »
Hi nidud,

while comparing real4 and real8 (.if (r4_1 == r4_2) ...    .if (r8_1 == r8_2) ...) works in 32 and 64 bit, it fails for real10. Likewise comparing a qword to an immediate value works in 64 bit but it fails in 32 bit ( .if (q == 255) ...). Comparing floats to immediate values is not possible at all (.if (r4 == 1.0) ...).

It would be extremely convenient to have all of these too (being able to compare a qword to an immediate value in 32 bit in first place) - any chance for an implementation?

Thanks

JK

jj2007

  • Member
  • *****
  • Posts: 11768
  • Assembler is fun ;-)
    • MasmBasic
Re: Large integers and floats
« Reply #48 on: September 19, 2021, 08:31:01 PM »
Fcmp: the desired precision can be indicated as third argument: low, medium, high, top and xtra precision

In practice, there is no such thing as "equal" when comparing two REAL10 values :cool:

nidud

  • Member
  • *****
  • Posts: 2307
    • https://github.com/nidud/asmc
Re: Large integers and floats
« Reply #49 on: September 20, 2021, 12:58:21 AM »
Note that the support for this is rather limited as it only support COMISS and COMISD.

while comparing real4 and real8 (.if (r4_1 == r4_2) ...    .if (r8_1 == r8_2) ...) works in 32 and 64 bit,

This didn't really work as it only compared the binary value (mov eax,r4_2 | cmp r4_1, eax). This is now added so now this works:

    .if (r4_1 > r4_2)
    *    movss xmm0, r4_1
    *    comiss xmm0,  r4_2
    *   jna @C0002

        nop
    .endif
    .if (r8_1 > r8_2)
        nop
    .endif
    .if (xmm0 > r4_2)
        nop
    .endif
    .if (xmm0 > r8_2)
        nop
    .endif

Immediate values needs register and size.

    option float: 4 ; default

    .if (xmm0 > 4.0)
        nop
    .endif

    option float: 8

    .if (xmm0 > 8.0)
        nop
    .endif

nidud

  • Member
  • *****
  • Posts: 2307
    • https://github.com/nidud/asmc
Re: Large integers and floats
« Reply #50 on: September 20, 2021, 04:23:44 AM »
Technically immediate floats are memory operands so they are now recursively parsed.

    .if ( r8_1 > 8.0 && r8_1 < r8_2 ||
          r4_1 > 4.0 && r4_1 < r4_2 ||
          xmm1 > r8_1 && xmm0 > r4_1 )
        nop
    .endif

        movsd   xmm0, qword ptr [ebp-10H]
        comisd  xmm0, qword ptr [_F0000]
        jbe     ?_001                   
        movsd   xmm0, qword ptr [ebp-10H]
        comisd  xmm0, qword ptr [ebp-18H]
        jc      ?_003                   
?_001:  movss   xmm0, dword ptr [ebp-4H]
        comiss  xmm0, dword ptr [_F0001]
        jbe     ?_002                   
        movss   xmm0, dword ptr [ebp-4H]
        comiss  xmm0, dword ptr [ebp-8H]
        jc      ?_003
?_002:  comisd  xmm1, qword ptr [ebp-10H]
        jbe     ?_004
        comiss  xmm0, dword ptr [ebp-4H]
        jbe     ?_004
?_003:  nop

JK

  • Member
  • **
  • Posts: 156
Re: Large integers and floats
« Reply #51 on: September 20, 2021, 07:29:25 AM »
Many thanks nidud!

Quote
Technically immediate floats are memory operands

If all immediate numbers are memory operands, then you could implement a qword comparison in 32 bit as two dword comparisons. This (pseudo code) would test for equal:
Code: [Select]
cmp dword ptr var, dword ptr [_F0000]
jne ...
cmp dword prt var[4], dword ptr [_F0000][4]
jne ...

one operand being a qword variable (of course it cannot be a register in 32 bit) and the other one an immediate value

JK

nidud

  • Member
  • *****
  • Posts: 2307
    • https://github.com/nidud/asmc
Re: Large integers and floats
« Reply #52 on: September 20, 2021, 08:16:11 AM »
If all immediate numbers are memory operands, then you could implement a qword comparison in 32 bit as two dword comparisons.

Only floats are converted so a qword needs to be assigned.

    local a:qword, b:qword

        .if ( a > b )

        .endif

        .new t:qword = 0x4000000000000000

        .if ( a < t )

        .endif

There is however a problem with signed compare of qwords in 32-bit.

JK

  • Member
  • **
  • Posts: 156
Re: Large integers and floats
« Reply #53 on: September 30, 2021, 12:42:25 AM »
Converting and comparing floats:

I know comparing floats can be tricky, but given the code below both real8 (g_r8 vs. l_r8_1/l_r8_2) should be equal, because the "input" (17.6) is equal:
Code: [Select]
option wstring:ON

include windows.inc
includelib kernel32.lib
includelib user32.lib


.data


g_r4  real4  17.6
g_r8  real8  17.6
g_r10 real10 17.6


.code


start proc
;***************************************************************************
; main
;***************************************************************************
local l_r4 :real4
local l_r8_1 :real8
local l_r8_2 :real8
local l_r10:real10


  finit
  fld  g_r4                                           ;get real4
  fstp l_r8_1                                         ;save as real8

  movss xmm0, g_r4                                    ;get real4
  cvtss2sd xmm1, xmm0                                 ;convert to real8
  movsD  l_r8_2, xmm1                                 ;save


  .if (l_r8_1 == l_r8_2)                              ;is equal, which correct
     MessageBoxW(0, "ok #1", "Test #1", 0)           
  .endif 

  .if (g_r8 == l_r8_2)                                ;should be equal, but isn´t
     MessageBoxW(0, "ok #2", "Test #1", 0)             
  .endif 

  .if (g_r8 == l_r8_1)                                ;should be equal, but isn´t
     MessageBoxW(0, "ok #3", "Test #1", 0)         
  .endif 


  invoke ExitProcess, 0
  ret

start endp

 
end start

The difference comes from the initialization, g_r8 is initialized with real8 precision, while only real4 precision (17.6) is given.

Ok - you could say: a real8 must be initialized with real8 precision, therefore i implicitly add trailing zeros, if not enough digits are given. But as you can see from the real4 to real8 conversion, both ways (FPU, xmm) don´t add zeros. The resulting numbers (l_r8_1 and l_r8_2) are equal (which is expected) but differ from g_r8 despite the fact, that all origin from exactly the same floating point number.

Is there a way to initialize g_r8 with real4 precision, so that the resulting value is equal to the value of a real4 to real8 conversion? Maybe something like:
Code: [Select]
g_r8 real8 17.6r4
where "r4" means: create a real4 number first then convert to real8 and then assign this value.

Thanks

JK

nidud

  • Member
  • *****
  • Posts: 2307
    • https://github.com/nidud/asmc
Re: Large integers and floats
« Reply #54 on: September 30, 2021, 01:43:00 AM »
the numbers:

real4  = 0x418CCCCD
real8  = 0x403199999999999A
real10 = 0x40038CCCCCCCCCCCCCCD

convert real4 to 8:

    movss xmm0,l_r4     ; <-- 418CCCCD
    cvtss2sd xmm0,xmm0  ; --> 40319999A0000000


It's possible to size-up a float by using real hex notation:

    value = 418CCCCDr
    .data
     g_r4 real4 value
     g_r8 real8 value
...
    movss xmm0,g_r4
    cvtss2sd xmm0,xmm0 
    movsd l_r8,xmm0
    .if ( l_r8 == g_r8 )
        printf("l_r8 == g_r8\n")
    .endif


Some reading:

https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

JK

  • Member
  • **
  • Posts: 156
Re: Large integers and floats
« Reply #55 on: September 30, 2021, 02:56:02 AM »
Thanks, so real hex notation is what i need, it indeed does what i want!

But how to get "418CCCCDr" from "17.6"? (i know it´s the memory representation of "17.6" in real4 format). This is, what "r4" (or whatever specifier) could do instead of having to do it (somehow) manually. You could even go one step further and add an option, which automatically makes an appropriate (according to the given precision) real hex out of a floating point number.
Code: [Select]
g_r4    real4 17.6     (= 0x418CCCCD)
g_r8_1 real8 17.6     (= 0x403199999999999A)
g_r8_2 real8 17.6r4   (= 0x40319999A0000000  from 418CCCCDr)
...
g_r10  real10 17.6r4
g_r10  real10 17.6123456789r8

option size-up:on
g_r8_1 real8 17.6    (= 0x40319999A0000000  from 418CCCCDr, size-up because only real4 precision is given)
g_r8_2 real8 17.6123456789 (-> no size-up needed, because real8 precision is given)
...
g_r10  real10 17.6                (-> size-up, because only real4 precision is given)
g_r10  real10 17.6123456789 (-> size-up, because only real8 precision is given)


Interesting reading - thanks!

JK

nidud

  • Member
  • *****
  • Posts: 2307
    • https://github.com/nidud/asmc
Re: Large integers and floats
« Reply #56 on: September 30, 2021, 03:38:59 AM »
It's difficult as it is and even more so with mixed size. The best method would probably be to compare the smallest size. Reduce all numbers to floats and maybe strip off the low byte as well.

    movd eax,xmm0
    and eax,0xFFFFFF00
    movd xmm0,eax

Quote
But how to get "418CCCCDr" from "17.6"?

Something like this maybe:

r4 macro value
    exitm<(value and 0FFFFFFFFE00000000000000000000000r)>
    endm

    .data
     g_r4 real4 r4(17.6)
     g_r8 real8 r4(17.6)

JK

  • Member
  • **
  • Posts: 156
Re: Large integers and floats
« Reply #57 on: October 01, 2021, 03:13:08 AM »
Yes- a macro would do. But in this case g_r4 real4 r4(17.6) is not equal to g_r4 real4 17.6, it is 17.599853 vs. 17.600000.

If i understand correctly, what you do here, then you try to mask out all mantissa bits exceeding real4 precision. But then the mask should be 32 + 7 (exponent difference = 7) bits "1" and the rest should be "0" (0FFFFFFFFFD000...) - which is closer (17.599998) but still not the same.

Loading both into the FPU results in 40038CCCCD0000000000 -> 17.600000.. vs. 40038CCCCC0000000000 -> 17.599998... It differs by one bit (the last one) set or not. Loading a real8 with a value of 17.6 results in 40038CCCCCCCCCCCD000, which explains the difference. So the basic idea (masking out) is good, but we also need some kind of rounding to make it correct.

The functionality must already be there (somewhere ) in ASMC, because just assigning 17.6 (converting a real16 back) to a real4 works correctly.

JK

nidud

  • Member
  • *****
  • Posts: 2307
    • https://github.com/nidud/asmc
Re: Large integers and floats
« Reply #58 on: October 01, 2021, 03:55:10 AM »
Think the first suggestion is probably more reliable than this bit manipulation.

cmpfd proto :real4, :real8 {
    cvtsd2ss xmm1,xmm1
    comiss xmm0,xmm1
    retm<(ZERO?)>
    }

    .data
     g_r4 real4 17.6
     g_r8 real8 17.6
...
    .if cmpfd(g_r4, g_r8)

jj2007

  • Member
  • *****
  • Posts: 11768
  • Assembler is fun ;-)
    • MasmBasic
Re: Large integers and floats
« Reply #59 on: October 01, 2021, 04:46:49 AM »