------------------------------------------------------------------------------------------------
                      [BUG/PRB.] ADJUST THE PRECISION OF THE FLOATING-POINT NUMBERS FOR THE ROUND() FUNCTION
                                January 2024
                 ------------------------------------------------------------------------------------------------
                                    CCB



1. BUG:

     There is a test program from Mr. Christof Wollenhaupt:

     *PROC testroundfunction

     ?ROUND(512.9250000000,2) && Displays 512.92
     ?ROUND(512.925000000,2) && Displays 512.93
     WAIT

     RETURN

     * END OF PROC TESTROUNDFUNCTION.

     We think they will display 512.93, but:
     ?ROUND(512.9250000000,2) && Displays 512.92


2. CAUSE:

     Please refer to the head of the testroundfunction.fxp:

     00000000:  FE F2 FF 22-02 01 00 00-00 D8 00 00-00 A1 00 00
     00000010:  00 37 00 00-00 00 00 00-00 00 00 00-00 00 00 00
     00000020:  00 00 00 00-00 00 00 3F-27 00 00 00-00 25 00 00
     00000030:  00 00 00 00-00 00 00 00-00 68 00 00-00 03 00 00
     00000040:  00 62 00 00-00 8F 56 82-47 1C 00 00-00 85 00 00
     00000050:  35 00 00 00-19 00 02 F8-03 01 FC 43-FA 0E 0A 66
     00000060:  66 66 66 66-07 80 40 F8-01 02 54 FD-FE 19 00 02
     00000070:  F8 03 01 FC-43 FA 0D 09-66 66 66 66-66 07 80 40
     00000080:  F8 01 02 54-FD FE 03 00-55 00 00 91-01 91 01 31
     00000090:  00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00

     the compiled p-code for the statement "?ROUND(512.9250000000,2)" from 0x00000054:
     19 00 02 F8 03 01 FC 43 FA 0E 0A 66 66 66 66 66 07 80 40 F8 01 02 54 FD FE
     --------------------------------------------------------------------------
     The width of the floating-point number is 0x0E (dec: 14).
     The number of decimal places is 0x0A (dec: 10).
     The 64-bit floating-point number is 0x4080076666666666.

     the compiled p-code for the statement "?ROUND(512.925000000,2)" from 0x0000006D:
     19 00 02 F8 03 01 FC 43 FA 0D 09 66 66 66 66 66 07 80 40 F8 01 02 54 FD FE
     --------------------------------------------------------------------------
     The width of the floating-point number is 0x0D (dec: 13).
     The number of decimal places is 0x09 (dec: 9).
     The 64-bit floating-point number is 0x4080076666666666.

     They are the same 64-bit floating-point numbers:
     512.9250000000 = 0x4080076666666666
     512.925000000  = 0x4080076666666666
     the difference is the width of the floating-point number and the number of decimal places.

     For the details when VFP calculates ROUND(512.9250000000,2):

       512. 9249 9999 9999 9546 0
     +   0. 0000 0000 0000 0500 0 (n=10, eps = 0.5*10^(-(n+3)) = 5.0000000000000001520E-14)
     ----------------------------
       512. 9249 9999 9999 9546 0

       512. 9249 9999 9999 9546 0
     * 100. 0000 0000 0000 0000 0
     ----------------------------
     51292. 4999 9999 9992 720
     +   0. 5000 0000 0000 0000 0
     ----------------------------
     51292. 9999 9999 9992 720

     floor(51292. 9999 9999 9992 720) = 51292.000000000000000

     51292. 0000 0000 0000 0000 0
     / 100. 0000 0000 0000 0000 0
     ----------------------------
       512. 9199 9999 9999 9590 0
     +   0. 0000 0500 0000 0000 0 (n=2, eps = 0.5*10^(-(n+3)) = 5.0000000000000004090E-06)
     ----------------------------
       512. 9200 0499 9999 9464 0

     So ROUND(512.9250000000,2) = 512.92.

     For the details when VFP calculates ROUND(512.925000000,2):

       512. 9249 9999 9999 9546 0
     +   0. 0000 0000 0000 4999 9 (n=9, eps = 0.5*10^(-(n+3)) = 4.9999999999999999000E-13)
     ----------------------------
       512. 9250 0000 0000 4092 0

       512. 9250 0000 0000 4092 0
     * 100. 0000 0000 0000 0000 0
     ----------------------------
     51292. 5000 0000 0043 660
     +   0. 5000 0000 0000 0000 0
     ----------------------------
     51293. 0000 0000 0043 660

     floor(51293. 0000 0000 0043 660) = 51293.000000000000000

     51293. 0000 0000 0000 0000 0
     / 100. 0000 0000 0000 0000 0
     ----------------------------
       512. 9299 9999 9999 9500 0
     +   0. 0000 0500 0000 0000 0 (n=2, eps = 0.5*10^(-(n+3)) = 5.0000000000000004090E-06)
     ----------------------------
       512. 9300 0499 9999 9374 0

     So ROUND(512.925000000,2) = 512.93.

     If we enable adjusting the precision of the floating-point numbers in Visual FoxPro Advanced,
     Visual FoxPro Advanced will set 15 valid digits for the following functions:
     ROUND() Function.

     Visual FoxPro Advanced will adjust the floating-point numbers before call the floor() function:

      51292. 9999 9999 9992 720
     +00000. 0000 0000 005
     --------------------------
      51293. 0000 0000 0042 720

     floor(51292. 9999 9999 9992 720 +00000. 0000 0000 005) = 51293.000000000000000

     So ROUND(512.9250000000,2) = 512.93.

      51293. 0000 0000 0043 660
     +00000. 0000 0000 005
     --------------------------
      51293. 0000 0000 0093 660

     floor(51293. 0000 0000 0043 660 +00000. 0000 0000 005) = 51293.000000000000000

     So ROUND(512.925000000,2) = 512.93.

     Visual FoxPro Advanced will also set 15 valid digits and round the value to integer when subtract two datetimes
     to get the difference between them in seconds.

     For the test program from Mr. Sergey Karimov:

     lnsec= {^2000/01/01 01:01:00 AM}-{^2000/01/01 00:00:00 AM}
     a1= lnsec%3600
     a2= a1/60
     ?lnsec, a1, a2, FLOOR(a1)/60, FLOOR(a1/60)

     Now it can run fine if we enable adjusting the precision of the floating-point numbers in Visual FoxPro Advanced.


3. RESOLUTION:

     We can write some code to fix the BUG.

     Label4ae942 ::
             push ebp                                                        ;0x004ae942 :        55
             mov ebp , esp                                                   ;0x004ae943 :        8bec
             and esp , 0FFFFFFF8h                                            ;0x004ae945 :        83e4f8
             sub esp , 018h                                                  ;0x004ae948 :        83ec18
             push ebx                                                        ;0x004ae94b :        53
             push ebp                                                        ;0x004ae94c :        55
             push esi                                                        ;0x004ae94d :        56
             push edi                                                        ;0x004ae94e :        57
             mov  dword ptr [ esp + 20 ] , 00h                               ;0x004ae94f :        c744241400000000
             call Fun420d8c                                                  ;0x004ae957 :        e83024f7ff
             mov esi , eax                                                   ;0x004ae95c :        8bf0
             lea ebx ,  dword ptr [ esi + 44 ]                               ;0x004ae95e :        8d5e2c
             push ebx                                                        ;0x004ae961 :        53
             lea edi ,  dword ptr [ esp + 32 ]                               ;0x004ae962 :        8d7c2420
             call Fun420dd3                                                  ;0x004ae966 :        e86824f7ff
             test eax , eax                                                  ;0x004ae96b :        85c0
             jne Label4aeaaf                                                 ;0x004ae96d :        0f853c010000
             mov edi , ebx                                                   ;0x004ae973 :        8bfb
             call Fun529a6e                                                  ;0x004ae975 :        e8f4b00700
             cmp  byte ptr [ebx] , 049h                                      ;0x004ae97a :        803b49
             mov  ebp , dword ptr [ ebx + 12 ]                               ;0x004ae97d :        8b6b0c
             mov edi , 0385h                                                 ;0x004ae980 :        bf85030000
             jne Label591cc0                                                 ;0x004ae985 :        0f8535330e00

     Label4ae98b ::
             mov  al , byte ptr [esi]                                        ;0x004ae98b :        8a06
             xor ecx , ecx                                                   ;0x004ae98d :        33c9
             cmp al , 059h                                                   ;0x004ae98f :        3c59
             sete cl                                                         ;0x004ae991 :        0f94c1
             test ecx , ecx                                                  ;0x004ae994 :        85c9
             mov  dword ptr [ esp + 24 ] , ecx                               ;0x004ae996 :        894c2418
             jne Label591ce8                                                 ;0x004ae99a :        0f8548330e00

     Label4ae9a0 ::
             cmp al , 049h                                                   ;0x004ae9a0 :        3c49
             je Label591cf6                                                  ;0x004ae9a2 :        0f844e330e00
             cmp al , 059h                                                   ;0x004ae9a8 :        3c59
             je Label591d0b                                                  ;0x004ae9aa :        0f845b330e00
             cmp al , 04Eh                                                   ;0x004ae9b0 :        3c4e
             jne Label591d26                                                 ;0x004ae9b2 :        0f856e330e00

     Label4ae9b8 ::
             test ebp , ebp                                                  ;0x004ae9b8 :        85ed
             jl Label591d4e                                                  ;0x004ae9ba :        0f8c8e330e00
             mov  eax , dword ptr [ esi + 8 ]                                ;0x004ae9c0 :        8b4608
             cmp eax , ebp                                                   ;0x004ae9c3 :        3bc5
             mov ecx , eax                                                   ;0x004ae9c5 :        8bc8
             jle Label591d60                                                 ;0x004ae9c7 :        0f8e93330e00

     Label4ae9cd ::
             cmp ecx , 012h                                                  ;0x004ae9cd :        83f912
             jnl Label591d6e                                                 ;0x004ae9d0 :        0f8d98330e00
             cmp eax , ebp                                                   ;0x004ae9d6 :        3bc5
             jle Label591d67                                                 ;0x004ae9d8 :        0f8e89330e00
             mov edx , eax                                                   ;0x004ae9de :        8bd0

     Label4ae9e0 ::
             cmp ebp , 012h                                                  ;0x004ae9e0 :        83fd12
             jg Label591d78                                                  ;0x004ae9e3 :        0f8f8f330e00

     Label4ae9e9 ::
             mov  ax , word ptr [ esi + 22 ]                                 ;0x004ae9e9 :        668b4616
             test ax , ax                                                    ;0x004ae9ed :        6685c0
             lea edi ,  dword ptr [ esi + 16 ]                               ;0x004ae9f0 :        8d7e10
             je Label477a2f                                                  ;0x004ae9f3 :        0f843690fcff
             test ah , ah                                                    ;0x004ae9f9 :        84e4
             js Label591d82                                                  ;0x004ae9fb :        0f8881330e00
             mov eax , 01h                                                   ;0x004aea01 :        b801000000

     Label4aea06 ::
             xor ebx , ebx                                                   ;0x004aea06 :        33db
             test eax , eax                                                  ;0x004aea08 :        85c0
             mov  eax , dword ptr [ esp + 24 ]                               ;0x004aea0a :        8b442418
             setl bl                                                         ;0x004aea0e :        0f9cc3
             test eax , eax                                                  ;0x004aea11 :        85c0
             jne Label4aea1c                                                 ;0x004aea13 :        7507
             mov ecx , edi                                                   ;0x004aea15 :        8bcf
             call Fun53e46a                                                  ;0x004aea17 :        e84efa0800

     Label4aea1c ::
             test ebx , ebx                                                  ;0x004aea1c :        85db
             fld qword ptr [edi]                                             ;0x004aea1e :        dd07
             jne Label591d9f                                                 ;0x004aea20 :        0f8579330e00

     Label4aea26 ::
             mov  eax , dword ptr [ esp + 20 ]                               ;0x004aea26 :        8b442414
             fld qword ptr [ 8 * ebp + offset Data922520 ]                            ;0x004aea2a :        dd04ed20259200
             sub esp , 08h                                                   ;0x004aea31 :        83ec08
             fstp qword ptr [ esp + 40 ]                                     ;0x004aea34 :        dd5c2428
             test eax , eax                                                  ;0x004aea38 :        85c0
             jne Label591da6                                                 ;0x004aea3a :        0f8566330e00
             fld qword ptr [ esp + 40 ]                                      ;0x004aea40 :        dd442428
             fmul st(0) , st(1)                                              ;0x004aea44 :        d8c9


     ;
     ;                 ---------------------------------------------------------------------------------------------------
     ;                      VFP 9.0 FIX - ADJUST THE PRECISION OF THE FLOATING-POINT NUMBERS FOR THE ROUND() FUNCTION
     ;                                February 2017
     ;                 ---------------------------------------------------------------------------------------------------
     ;                                     CCB
     ;
     ; Sometimes the ROUND() function returns wrong result, for example,
     ; ?ROUND(512.9250000000,2) && Displays 512.92
     ; ?ROUND(512.925000000,2) && Displays 512.93
     ;
     ; 2017/2/26, by ccb
     ;

             cmp dword ptr vfpa_sys9001_data,00h
             je Label4aea46

             fld st(0)
             fabs
             fldlg2
             fxch st(1)
             fyl2x

             fld st(0)
             fistp dword ptr [ esp ]
             mov ecx,dword ptr [ esp ]
             cmp ecx,80000000h
             je Label4aea45
             and ecx,80000000h
             test ecx,ecx
             jne Label4aea45

             mov eax,0
             IFDEF RAX
             push esi
             lea esi ,  dword ptr [ Data922438 ]
             fadd qword ptr [ 8 * eax + esi ]
             pop esi
             ELSE
             fadd qword ptr [ 8 * eax + offset Data922438 ]
             ENDIF

             mov eax,0Ah
             IFDEF RAX
             push esi
             lea esi ,  dword ptr [ Data922438 ]
             fadd qword ptr [ 8 * eax + esi ]
             pop esi
             ELSE
             fadd qword ptr [ 8 * eax + offset Data922438 ]
             ENDIF

             fistp dword ptr [ esp ]
             mov eax,0Fh
             sub eax,dword ptr [ esp ]
             cmp eax,0
             jle Label4aea46
             cmp eax,0Fh
             jge Label4aea46

             fld st(0)
             fistp dword ptr [ esp ]
             mov ecx,dword ptr [ esp ]
             cmp ecx,80000000h
             je Label4aea46
             and ecx,80000000h
             test ecx,ecx
             jne Label4aea44

             IFDEF RAX
             push esi
             lea esi ,  dword ptr [ Data922438 ]
             fadd qword ptr [ 8 * eax + esi ]
             pop esi
             ELSE
             fadd qword ptr [ 8 * eax + offset Data922438 ]
             ENDIF
             jmp Label4aea46

     Label4aea44 ::
             IFDEF RAX
             push esi
             lea esi ,  dword ptr [ Data922438 ]
             fsub qword ptr [ 8 * eax + esi ]
             pop esi
             ELSE
             fsub qword ptr [ 8 * eax + offset Data922438 ]
             ENDIF
             jmp Label4aea46

     Label4aea45 ::
             fstp st(0)
             jmp Label4aea46


     Label4aea46 ::
             fadd qword ptr [ Data9256b0 ]                                   ;0x004aea46 :        dc05b0569200
             fstp qword ptr [ esp ]                                          ;0x004aea4c :        dd1c24
             fstp st(0)                                                      ;0x004aea4f :        ddd8
             call  floor                                       ;0x004aea51 :        ff15a0799100
             fdiv qword ptr [ esp + 40 ]                                     ;0x004aea57 :        dc742428

     Label4aea5b ::
             add esp , 08h                                                   ;0x004aea5b :        83c408
             test ebx , ebx                                                  ;0x004aea5e :        85db
             jne Label591dc2                                                 ;0x004aea60 :        0f855c330e00

     Label4aea66 ::
             mov  eax , dword ptr [ esp + 24 ]                               ;0x004aea66 :        8b442418
             fstp qword ptr [edi]                                            ;0x004aea6a :        dd1f
             test eax , eax                                                  ;0x004aea6c :        85c0
             jne Label591dc9                                                 ;0x004aea6e :        0f8555330e00
             mov  eax , dword ptr [ esp + 20 ]                               ;0x004aea74 :        8b442414
             test eax , eax                                                  ;0x004aea78 :        85c0
             jne Label591e1c                                                 ;0x004aea7a :        0f859c330e00
             mov  eax , dword ptr [ esi + 8 ]                                ;0x004aea80 :        8b4608
             cmp ebp , eax                                                   ;0x004aea83 :        3be8
             ja Label591e28                                                  ;0x004aea85 :        0f879d330e00

     Label4aea8b ::
             cmp ebp , 012h                                                  ;0x004aea8b :        83fd12
             jnl Label591e42                                                 ;0x004aea8e :        0f8dae330e00

     Label4aea94 ::
             mov  dword ptr [ esi + 8 ] , ebp                                ;0x004aea94 :        896e08

     Label4aea97 ::
             mov  eax , dword ptr [ esi + 4 ]                                ;0x004aea97 :        8b4604
             cmp eax , 028h                                                  ;0x004aea9a :        83f828
             jnl Label4aeaa3                                                 ;0x004aea9d :        7d04
             inc eax                                                         ;0x004aea9f :        40
             mov  dword ptr [ esi + 4 ] , eax                                ;0x004aeaa0 :        894604

     Label4aeaa3 ::
             mov  eax , dword ptr [ esp + 28 ]                               ;0x004aeaa3 :        8b44241c
             test eax , eax                                                  ;0x004aeaa7 :        85c0
             jne Label591e4c                                                 ;0x004aeaa9 :        0f859d330e00

     Label4aeaaf ::
             pop edi                                                         ;0x004aeaaf :        5f
             pop esi                                                         ;0x004aeab0 :        5e
             pop ebp                                                         ;0x004aeab1 :        5d
             pop ebx                                                         ;0x004aeab2 :        5b
             mov esp , ebp                                                   ;0x004aeab3 :        8be5
             pop ebp                                                         ;0x004aeab5 :        5d
             ret                                                             ;0x004aeab6 :        c3


4. APPLIES TO:

     VFP 6.0.8167.0
     VFP 6.0.8961.0 (SP5)

     VFP 7.0.0.9262
     VFP 7.0.0.9465 (SP1)

     VFP 8.0.0.2521
     VFP 8.0.0.3117 (SP1)

     VFP 9.0.0.2412
     VFP 9.0.0.3504 (SP1)
     VFP 9.0.0.4611 (SP2)
     VFP 9.0.0.5015 (SP2)
     VFP 9.0.0.5411 (SP2)
     VFP 9.0.0.5721 (SP2)
     VFP 9.0.0.5815 (SP2)
     VFP 9.0.0.6303 (SP2)
     VFP 9.0.0.6602 (SP2)
     VFP 9.0.0.7423 (SP2)

     The bug has been fixed in VFP Advanced.


5. REFERENCE WEBSITES:

     1, baiyujia.com:
     http://www.baiyujia.com
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix23.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix97.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix98.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix123.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix124.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix255.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix256.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix257.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix258.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix259.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix260.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix303.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix304.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix305.asp
     http://www.baiyujia.com/vfpdocuments/f_vfp9fix306.asp

     2, microsoft.com:
     https://social.msdn.microsoft.com/Forums/en-US/cee551be-35f8-4ddc-a07e-51d2d7f79345/rounding-bug-in-vfp8
     https://social.msdn.microsoft.com/Forums/en-US/a8725753-5815-4d45-aa7b-bdead9277986/how-can-i-subract-datetime-values

     3, foxite.com:
     https://www.foxite.com/archives/round-problem-0000070917.htm
     https://www.foxite.com/archives/interesting-behavior-of-inputmask-0000406050.htm
     https://www.foxite.com/archives/round-0000223089.htm
     https://www.foxite.com/archives/how-to-compute-the-elapsed-time-in-foxpro-0000058524.htm
     https://www.foxite.com/archives/int-strange-behavior-0000445268.htm

     4, tek-tips.com:
     http://www.tek-tips.com/viewthread.cfm?qid=531793
     http://www.tek-tips.com/viewthread.cfm?qid=1738764

     5, foxpert.com:
     http://www.foxpert.com/foxpro/knowlbits/files/knowlbits_201102_1.html

     6, fox-id.org:
     http://www.fox-id.org/smf/general-code-activex-com/hasil-perhitungan-vfp-kok-beda-sama-kalkulator/


6. OTHER:

     For reference only, there is no guarantees.

     Any questions or suggestions, please send me an email at ccb2000@163.com.