News:

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

Current version of FPULIB.

Started by hutch--, November 28, 2010, 08:42:27 AM

Previous topic - Next topic

jj2007

Quote from: MichaelW on May 07, 2011, 08:40:10 PM
The link I have the problem with is here:

http://www.ray.masmcode.com/fpu.html#fpulib


No such problem here, it works. And files are identical to what I downloaded in August 2010, except for FPUlibtester.exe

raymond

Quote from: MichaelW on May 07, 2011, 10:31:59 PM
The only problems with negative exponents that I find are at the extreme small end of the REAL10 range.

3.400000E+0038
1.180000E-0038

1.790000000000000E+0308
2.230000000000000E-0308

1.180000000000000E+4932
ERROR
ERROR
3.359999999999999E-4917


Code and EXE in attachment.

And BTW I also tested with the 19 digits reduced to 15, and this did not change the results.


Thanks for that test Michael. And testing with 15 of 19 digits would not make any difference because the function, as written, immediately reduces anything higher than 15 to that limit.

I've found the reason why an error occurs in that range and E-4917 would be the smallest without getting an error. This will be corrected within the next few days. The possibility of converting denormalized REAL10 numbers will also be included.

I wonder if ratu's post had anything to do with this flaw. :red
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

dedndave

QuoteI wonder if ratu's post had anything to do with this flaw.  :red

:bg
we knew you'd find the bug, Ray   :U

raymond

#18
The reason why the FpuFLtoA function was returning an error for values smaller than 3.36e-4917 was due to the following factors in the conversion algo:
i) The value needs to be converted to what would be an 18-digit integer before it is dumped to memory as a packed BCD with the "fbstp" instruction.
ii) The log10 of the value is computed to get the power of 10 required to express it in scientific notation.
iii) But using the reverse of that power of 10 would only provide a single digit for the integer portion. The number of decimal digits specified must also be added to obtain all the required digits for proper display.

For example, if the value to be converted was 5.123456789e-65 and the converted value was to be displayed with 6 decimal digits, it would have to be multiplied by 10(6+65) to get an integer with 7 digits, i.e. 5123457. However, if the value to be converted was 5.123456789e-4930, it would have to be multiplied by 10(6+4930) which exceeds the upper range of the FPU. This resulted in an invalid operation which was detected by the algo causing the ERROR message to be displayed.

Remedy:
If the required power of 10 to convert the value to an 18-digit integer exceeds 4931, the excess is kept in a memory variable X and the value is first multiplied by 104931. If X!=0, that is then further multiplied by 10X before storing the packed BCD with the required number of digits.

The Fpulibtester in the new zip file was also updated to correct the broken scientific notation. Any resulting value lower than 10-15 also gets displayed in scientific notation as default regardless of the choice with the radio buttons. The FpuSinh function also needed a minor correction to display the required value.

This latest version of the Fpulib v2-34 is available as usual from my site
http://www.ray.masmcode.com/fpu.html#fpulib
and the zipped file is also attached.
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

jj2007

Raymond,

Your FpuLib is a fantastic resource. Thanks for the tremendous efforts that have gone into this library.

:U

raymond

When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

MichaelW

Well, I hope I'm doing something wrong, but while the changes fixed the problems at the small end of the REAL10 range, positive exponents at the large end of all three ranges, values that previously worked, don't work now. For a REAL8, the largest workable value, determined by trial, appears to be just below 10.0e15.

;================================================================================
    include \masm32\include\masm32rt.inc
    include fpu.inc
    includelib fpu.lib
;================================================================================
; Approximate limits per Simply Fpu:
; REAL4: 3.4e38, 1.17e-38
; REAL8: 1.79e308, 2.22e-308
; REAL10: 1.19e4932, 3.36e-4932
; Approximate limits per MASM 6.0 Programmers Guide:
; REAL4: 3.4e38, 1.18e-38
; REAL8: 1.79e308, 2.23e-308
; REAL10: 1.18e4932, 3.37e-4932
;================================================================================
    .data

        r4_0      REAL4 3.40e38
        r4_1      REAL4 1.18e-38

        r8_00     REAL8 9.99999999999999e15
        r8_01     REAL8 10.0e15
        r8_0      REAL8 1.79e308
        r8_1      REAL8 2.23e-308

        r10_0     REAL10 1.18e4932
        r10_1     REAL10 3.37e-4932

        buff      db     40 dup(0)

    .code
;================================================================================
start:
;================================================================================

    invoke FpuFLtoA, ADDR r4_0, 6, ADDR buff, SRC1_REAL4 or SRC2_DIMM or STR_SCI
    print ADDR buff,13,10

    invoke FpuFLtoA, ADDR r4_1, 6, ADDR buff, SRC1_REAL4 or SRC2_DIMM or STR_SCI
    print ADDR buff,13,10,13,10

    invoke FpuFLtoA, ADDR r8_00,15, ADDR buff, SRC1_REAL8 or SRC2_DIMM or STR_SCI
    print ADDR buff,13,10

    invoke FpuFLtoA, ADDR r8_01,15, ADDR buff, SRC1_REAL8 or SRC2_DIMM or STR_SCI
    print ADDR buff,13,10

    invoke FpuFLtoA, ADDR r8_0, 15, ADDR buff, SRC1_REAL8 or SRC2_DIMM or STR_SCI
    print ADDR buff,13,10

    invoke FpuFLtoA, ADDR r8_1, 15, ADDR buff, SRC1_REAL8 or SRC2_DIMM or STR_SCI
    print ADDR buff,13,10,13,10

    invoke FpuFLtoA, ADDR r10_0, 15, ADDR buff, SRC1_REAL or SRC2_DIMM or STR_SCI
    print ADDR buff,13,10

    invoke FpuFLtoA, ADDR r10_1, 15, ADDR buff, SRC1_REAL or SRC2_DIMM or STR_SCI
    print ADDR buff,13,10,13,10



    inkey "Press any key to exit..."
    exit
;================================================================================
end start


ERROR
1.180000E-0038

9.999999999999990E+0015
ERROR
ERROR
2.230000000000000E-0308

ERROR
3.370000000000000E-4932

eschew obfuscation

raymond

 :red :red A real newbie error. :red :red

Unless the .if statement is qualified, the comparison is unsigned. That's what happened in the short added code. :snooty: If the value to be converted would exceed a 16-digit integer, it gets displlayed by default in the scientific notation. It thus has to be multiplied by a negative power of 10 to reduce its size before converting it to a packed BCD. The unsigned comparison of that negative power with 4931 was obviously the wrong thing to do. :eek

The attachement in my previous post has been edited with the revised zip file containing the corrected function. The included Fpulibtester has also been recompiled with the corrected code.

The corrected file was also uploaded to my site. My apology for any inconvenience. And, many thanks again Michael for picking that up so rapidly.
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

MichaelW

This turned out to be more complex, and slower, than what I originally envisioned. It should be possible to do this same basic thing with a REAL4, or with an appropriate CRT a REAL10.

;==============================================================================
    include \masm32\include\masm32rt.inc
    include fpu.inc
    includelib fpu.lib
;==============================================================================

printf MACRO format:REQ, args:VARARG
    IFNB <args>
        invoke crt_printf, cfm$(format), args
    ELSE
        invoke crt_printf, cfm$(format)
    ENDIF
    EXITM <>
ENDM

;==============================================================================
    .data
        r8      REAL8 ?
        coef1   dq 0
        coef2   dq 0
        exp1    dd 0
        exp2    dd 0
        buff1   db 40 dup(0)
        szcoef1 db 40 dup(0)
        szexp1  db 10 dup(0)
        buff2   db 40 dup(0)
        szcoef2 db 40 dup(0)
        szexp2  db 10 dup(0)
    .code
;==============================================================================

rand_r8 proc
  @@:
    invoke nrandom, -1
    push eax
    invoke nrandom, -1
    push eax
    fld REAL8 PTR [esp]           ; load 64-bit random integer as REAL8
    add esp, 8
    fstp r8
    invoke FpuExam, ADDR r8, SRC1_REAL8
    test eax, XAM_VALID
    jz  @B                        ; try again if invalid
    fld r8
    ret
rand_r8 endp

;==============================================================================
start:
;==============================================================================
    xor ebx, ebx
    .WHILE ebx < 5000000

      invoke rand_r8
      fstp r8

      invoke FpuFLtoA,ADDR r8,15,ADDR buff1,SRC1_REAL8 or SRC2_DIMM or STR_SCI
      ;printf( "%s\t", ADDR buff1 )

      ;----------------------------------------------------
      ; Extract coefficient and convert to 64-bit integer.
      ;----------------------------------------------------

      invoke crt_strtok, ADDR buff1, chr$(".Ee")
      invoke crt_strcpy, ADDR szcoef1, eax
      ;printf( "%s\t", ADDR szcoef1 )
      invoke crt_strtok, 0, chr$(".Ee")
      invoke crt_strcat, ADDR szcoef1, eax
      ;printf( "%s\t", ADDR szcoef1 )
      invoke crt__atoi64, ADDR szcoef1
      ;printf( "%I64d\t", edx::eax )
      mov DWORD PTR coef1+4, edx
      mov DWORD PTR coef1, eax
      ;printf( "%I64d\t", coef1 )

      ;-------------------------------------------------
      ; Extract exponent and convert to 32-bit integer.
      ;-------------------------------------------------

      invoke crt_strtok, 0, chr$(".Ee")
      invoke crt_strcpy, ADDR szexp1, eax
      ;printf( "%s\n", ADDR szexp1 )
      invoke crt_atoi, ADDR szexp1
      mov exp1, eax

      invoke crt_sprintf, ADDR buff2, cfm$("%.15e"), r8
      ;printf( "%s\t", ADDR buff2 )

      invoke crt_strtok, ADDR buff2, chr$(".Ee")
      invoke crt_strcpy, ADDR szcoef2, eax
      ;printf( "%s\t", ADDR szcoef2 )
      invoke crt_strtok, 0, chr$(".Ee")
      invoke crt_strcat, ADDR szcoef2, eax
      ;printf( "%s\t", ADDR szcoef2 )
      invoke crt__atoi64, ADDR szcoef2
      ;printf( "%I64d\t", edx::eax )
      mov DWORD PTR coef2+4, edx
      mov DWORD PTR coef2, eax
      ;printf( "%I64d\t", coef2 )

      invoke crt_strtok, 0, chr$("Ee")
      invoke crt_strcpy, ADDR szexp2, eax
      ;printf( "%s\n", ADDR szexp2 )
      invoke crt_atoi, ADDR szexp2
      mov exp2, eax

      ;--------------------------------------------------
      ; Compare the coefficients and display an error if
      ; the absolute value of the difference exceeds 1.
      ;--------------------------------------------------

      mov eax, DWORD PTR coef1+4
      cmp eax, DWORD PTR coef2+4
      jne coef_error
      push DWORD PTR coef1
      mov eax, DWORD PTR coef1
      sub eax, DWORD PTR coef2
      pop DWORD PTR coef1
      invoke crt_abs, eax
      cmp eax, 1
      ja  coef_error
      ;printf( "\nabs %d\n", eax )
      jmp @F
    coef_error:
      printf( "ERROR %I64d\t%I64d\n", coef1, coef2 )
    @@:

      ;--------------------------------------
      ; Compare the exponents and diaplay an
      ; error if they are not identical.
      ;--------------------------------------

      mov eax, exp1
      cmp eax, exp2
      je  @F
      printf( "ERROR %d\t%d\n", exp1, exp2 )
    @@:

      inc ebx
      test ebx, 0ffffh
      jnz @F
      printf( "." )
    @@:
    .ENDW
    printf( "\n\n" )

    inkey "Press any key to exit..."
    exit
;==============================================================================
end start


eschew obfuscation