-------------------------------------------------
                        VFP 9.0 FIX - ROUND() Function
                                February 2017
                 -------------------------------------------------
                                    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 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 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 calculate 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 calculate 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 set enable adjust 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,
     INT() 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 set enable adjust the precision of the floating-point numbers in Visual FoxPro Advanced.


3. RESOLUTION:

     We can write some code to fix the BUG.

     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 - 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
             fadd qword ptr [ 8 * eax + offset Data922438 ]

             mov eax,10
             fadd qword ptr [ 8 * eax + offset Data922438 ]

             fistp dword ptr [ esp ]
             mov eax,15
             sub eax,dword ptr [ esp ]
             cmp eax,0
             jle Label4aea46
             cmp eax,15
             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

             fadd qword ptr [ 8 * eax + offset Data922438 ]
             jmp Label4aea46

     Label4aea44 ::
             fsub qword ptr [ 8 * eax + offset Data922438 ]
             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

     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.org:
     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 e-mail to ccb2000@163.com.