Announcement

Collapse
No announcement yet.

%exception_flt_inexact_result

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • %exception_flt_inexact_result

    Hi all,


    i have been using a self written addon to a commercial banking
    application for years now without any problems. Since adding a few
    changes and compiling with pb9, i get erratic crashes. I cannot
    reproduce them. Sometimes it crashes three times in row, sometimes
    it runs a hundred times without crash. According to the GPF box
    an exception %EXCEPTION_FLT_INEXACT_RESULT occurs in the main exe
    of the commerical software - and not in my dll! Yet it must be in my
    code, because without it, the commercial app runs perfectly well.
    I don´t use any COM here, just plain sdk style code. I cannot post
    code here because it is large project (several thousand lines of
    code) containing personal and sensible data.

    After recompiling it with pb8 (pb8.04 to be exact) it works without
    problems again. I don´t think of a compiler error in the first place,
    it is more likely some badly or anbigously coded lines the new
    compiler interpets different than the previous version. I thoroughly
    did RTFM for pb9, but still have no idea, what could be a reason for
    this.

    0xC000008F = %EXCEPTION_FLT_INEXACT_RESULT = The result of a floating-
    point operation cannot be represented exactly as a decimal fraction
    - not what one would get normally.

    I am familiar with these, know what to look for and how to handle it
    %STATUS_ACCESS_VIOLATION = &HC0000005???
    %STATUS_FLOAT_DIVIDE_BY_ZERO = &HC000008E???
    %STATUS_INTEGER_DIVIDE_BY_ZERO = &HC0000094???
    %STATUS_STACK_OVERFLOW = &HC00000FD???

    But what exactly is %EXCEPTION_FLT_INEXACT_RESULT, how could it occur
    what code is likely to produce such exceptions ?


    Any ideas, any help appreciated,


    thanks


    guenther

  • #2
    Official explanation from SDK:
    " The result of a floating-point operation cannot be represented exactly as a decimal fraction"

    What it means:
    I have no clue; many many floating point operations cannot be represented as a decimal fraction.

    Best guesses:
    1. Doing some assembly-language math, impermissibly mixing floating point instructions with decimal-only instructions.

    2. You have corrupted your memory, placing invalid bits/characters into a floating-point operand which is needed later. THere are, of course, lots of ways to corrupt your memory, many of which require very little effort....array bounds violations, wrong pointer vars, incorrectly defined UNIONs, TYPE SET out of range of target variable....

    3. Main program doing #1 because it did not expect your function to do #2.


    MCM
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      Guenther,
      the FPU has a number of exception conditions and INEXACT RESULT is one of them. Others are underflow, overflow, divide by zero, denormalised operand and invalid operation.
      These are usually masked in the FPU control register so they don't cause an exception.

      In certain types of computation it may be necessary to know that the result produced isn't just extremely close to the real result but is actually exact. In those very few circumstances you would unmask the FPU INEXACT RESULT exception flag (also called the PRECISION exception flag) and allow this condition to be reported.

      In normal circumstances you wouldn't use INEXACT RESULT exceptions because almost all "normal" calculations will cause the error.
      e.g. all of these will produce an inexact result:
      sine, COS, ATN, Log of almost anything
      1/3 or 1/7 or 22/31 and many other divisions
      SQRT(most things)
      Assigning an EXT or DOUBLE result to lower precision variable.


      I don't know if that'll help you narrow down the problem.

      Paul.

      Comment


      • #4
        Micheal, Paul,


        thanks for your quick answer. In fact i don´t do any higher math in my
        addon (except +,-,* and /), nor do i use any floating point data types
        (i use: ptr, byte, word, long, dword, string and asciiz - nothing else)

        In the meantime i´m pretty sure it is a question of pb8 versus pb9.
        What really upsets me, is the fact that i don´t know where and why and
        that there possibly are similar code snippets in other projects producing
        unwanted results if recompiled with pb9. You cannot be sure ...

        I don´t think there are logic bugs in my code anymore, otherwise it
        wouldn´t have run for several years now without problems. It could
        be some variables i return in many different functions. Maybe an implicit
        byval/byref problem or something like that, which is recompiled in a
        different manner with pb9 than before. Maybe i missed something in the docs.

        What are your experiences porting code from pb8 to pb9, what changes were
        necessary in your code in order to recompile/run with pb9, what problems
        did you encounter, and how did you solve them?


        My hope is, someone else stumbled over a similar thing and knows what to
        look for (revising several thousand lines of code having no clue at all
        is really though work ...)


        Thanks to all in advance for your contribution


        guenther

        Comment


        • #5
          Guenther
          Don't know if this helps but many other languages do more extinsive error checking than PB, had an instance where I was generating an overfow in a DLL but not actually clearing the error, so the next math operation in VB would report the error. Can't comment why in 9 and not 8 but if you havn't changed you code then some floating point or quad operation is sometimes setting that error flag in PB, which is not checking for it or clearing it and it is being detected later in the other language.
          John

          Comment


          • #6
            Bingo !

            John,


            your post brings up the right clue, as it seems !

            Armed with a disassembler and a debugger i found out this:
            - the gpf location in my "host" holds an fpu opcode (fstp)
            - a few lines down is one of numerous "interfaces" between
            my dll and my host
            - some more lines down i can read "fldcw" another fpu opcode
            resetting exception conditions in the fpu (thanks Paul)
            - running "version 8" fldcw is executed always, running "version 9"
            fldcw doesn´t get executed under all circumstances !!!

            This is not a matchpoint, because i still don´t know yet what
            exactly is going wrong. But at least i now have a very good theory
            to work upon. I have a raw guess where the problem could be located
            and - the more - i found a method to reproduce the crash now, which
            makes me quite sure i´m on the right track.


            Doing a binary diff on both versions of my dll showed reasonable
            differences - much more than i would have expected. From the filesize
            you can see Version 9 is about 10% longer (!?!) compiling from
            exactly the same sources (would be nice to know why?).



            I will post the solution to my riddle here asap.


            Thanks to all,


            guenther

            Comment


            • #7
              This is virtually certain to be a classic case of memory corruption. Some code in your program is overwriting needed data when it should not do so. This snowballs into additional unpleasant side effects later.

              Could be an array bounds error, an erroneous pointer, errant assembler code, a RETURN without a GOSUB, or something/anything else which changes memory that should not be changed. You need to find the source and cause of the memory change.

              A good start is to run the code with #DEBUG DISPLAY ON. This will notify you of untrapped array bounds errors.

              Best regards,

              Bob Zale
              PowerBASIC Inc.

              Comment


              • #8
                Does not work here

                Bob,


                a very good feature, but it does not work for me here!
                My addon stops initializing and aborts with #DEBUG DISPLAY ON.
                I checked this and many other things before. Some routines in fact are heavy asm code, maybe this is the reason. Using built in test features like #TOOLS,
                #DEBUG etc. seem to make my code not functional.

                To make one thing clear, this is a problem with my code not with Power Basic!
                In different circumstances these tools really have helped me a lot.
                As stated above, i don´t think it is a compiler bug it is more likely
                a problem with my code.

                I will track it down and report


                Thanks anyway


                guenther

                Comment


                • #9
                  Problem solved

                  To begin with, this is not a compiler bug. But the problem arises, because
                  pb9 compiles different code than pb8 does. The problem lies in how and
                  what for i use this compiled code!


                  Things will become clearer, if i give some more explanation beforehand:

                  I´m using a third party app, which does a lot of useful things for me.
                  To make my work much more efficient, i would like to have some more
                  info displayed in a certain screen of this app. Retrieving this additional
                  info is no problem at all, but this app offers no option to present the
                  necessary data needed in a way i need it. So i decided to to it - among
                  other things - by myself.

                  I do this by applying a runtime patch to its TextOut routine - to be exact,
                  i overwrite the address of a call to TextOut with the address of my own
                  routine - thus beeing able now to control screen output by myself (insert
                  additional info, change formatting, colorize, etc.)


                  this is sample code of my replacment routine. It does nothing useful here
                  than to call TextOut by itself


                  Code:
                  SUB dopaint
                  '***********************************************************************************************
                  ' paint 
                  '***********************************************************************************************
                  
                    pt_dopaint = CODEPTR(do_paint)                       'get a pointer to do_paint (runtime)
                  
                  
                  EXIT SUB
                  
                  
                  do_paint:                                              'replacement routine for rc textout
                  ! int 3                                                'for testing only
                  
                    !  mov eax, [esp+4]
                    !  mov pt_hdc, eax                   ;get hdc
                    !  mov eax, [esp+8]
                    !  mov pt_x  , eax                   ;get x
                    !  mov eax, [esp+&H0C]
                    !  mov pt_y  , eax                   ;get y
                    !  mov eax, [esp+&H10]
                    !  mov pt_txt, eax                   ;get pointer to text
                    !  mov eax, [esp+&H14]
                    !  mov pt_len, eax                   ;get textlength
                  
                    !  pushad
                  
                    !  push ebp
                    !  mov ebp, esp
                    !  sub esp, 20
                  
                  '  !  mov ax, &H133F
                  '  !  mov [ebp-&H10], ax
                  
                  
                  CALL textout(pt_hdc, pt_x, pt_y, @pt_txt, pt_len)  
                  
                  
                  ' ... other code here
                  
                  
                  '***********************************************************************************************
                  ' return and cleanup stack
                  '***********************************************************************************************
                  
                    !  add esp, 20
                    !  pop ebp
                  
                    !  popad
                  
                    !  ret 20                                            'five dword size parameters = 20 (decimal)
                  
                  
                  END SUB

                  this is pb8 output


                  Code:
                   sub_40D0C9      proc near               ; CODE XREF: LIBMAIN+4E0p
                                                           ; DATA XREF: sub_40D0C9+9o
                  
                   var_C           = dword ptr -0Ch
                  
                                   push    ebp
                                   mov     ebp, esp
                                   push    ebx
                                   push    esi
                                   push    edi
                                   sub     esp, 64h
                                   push    offset sub_40D0C9
                                   xor     esi, esi
                                   push    esi
                                   push    esi
                                   push    esi
                                   push    esi
                                   mov     eax, offset loc_40D0ED
                                   mov     dword ptr $L, eax ; "l"
                                   jmp     loc_40D165
                   ; ---------------------------------------------------------------------------
                  
                   loc_40D0ED:                             ; DATA XREF: sub_40D0C9+14o
                  <begin of replacment routine>
                                   int     3               ; Trap to Debugger
                                   mov     eax, [esp+4]
                                   mov     dword_433868, eax
                                   mov     eax, [esp+8]
                                   mov     dword_43386C, eax
                                   mov     eax, [esp+0Ch]
                                   mov     dword_433870, eax
                                   mov     eax, [esp+10h]
                                   mov     dword_433874, eax
                                   mov     eax, [esp+14h]
                                   mov     dword_433878, eax
                                   pusha
                                   push    ebp
                                   mov     ebp, esp
                                   sub     esp, 14h        ; reseve some extra stack space (not needed here) 
                                   push    dword_433878    ; int
                                   mov     ebx, dword_433874
                                   push    ebx             ; LPCSTR
                                   push    dword_433870    ; int
                                   push    dword_43386C    ; int
                                   push    dword_433868    ; HDC
                                   call    ds:TextOutA
                                   call    sub_431274
                                   add     esp, 14h
                                   pop     ebp
                                   popa
                                   retn    14h
                  
                  --------------------------------------------
                  
                   sub_431274      proc near               ; CODE XREF: sub_401AD7+1Dp
                                                           ; sub_401B1A+26p ...
                                   fldcw   cs:word_42BBF2  ; stack frame independant !!!
                                   retn
                   sub_431274      endp


                  this is pb9 output:


                  Code:
                   sub_40D650      proc near               ; CODE XREF: LIBMAIN+4CDp
                                                           ; DATA XREF: sub_40D650+Eo
                  
                   var_10          = word ptr -10h
                   var_C           = dword ptr -0Ch
                  
                                   push    ebp
                                   mov     ebp, esp
                                   push    ebx
                                   push    esi
                                   push    edi
                                   push    133Fh
                                   sub     esp, 70h
                                   push    offset sub_40D650
                                   xor     esi, esi
                                   push    esi
                                   push    esi
                                   push    esi
                                   push    esi
                                   mov     eax, offset loc_40D679
                                   mov     dword ptr $JpoJsJzLahfLds+14h, eax
                                   jmp     loc_40D6EF
                   ; ---------------------------------------------------------------------------
                  
                   loc_40D679:                             ; DATA XREF: sub_40D650+19o
                  <begin of replacment routine>
                                   int     3               ; Trap to Debugger
                                   mov     eax, [esp+4]
                                   mov     dword ptr $JpoJsJzLahfLds+18h, eax
                                   mov     eax, [esp+8]
                                   mov     dword ptr $JpoJsJzLahfLds+1Ch, eax
                                   mov     eax, [esp+0Ch]
                                   mov     dword ptr $JpoJsJzLahfLds+20h, eax
                                   mov     eax, [esp+10h]
                                   mov     dword ptr $JpoJsJzLahfLds+24h, eax
                                   mov     eax, [esp+14h]
                                   mov     dword ptr $JpoJsJzLahfLds+28h, eax
                                   pusha
                                   push    ebp
                                   mov     ebp, esp
                                   sub     esp, 14h        ; reseve some extra stack space (not needed here) 
                                   push    dword ptr $JpoJsJzLahfLds+28h ; int
                                   mov     ebx, dword ptr $JpoJsJzLahfLds+24h
                                   push    ebx             ; LPCSTR
                                   push    dword ptr $JpoJsJzLahfLds+20h ; int
                                   push    dword ptr $JpoJsJzLahfLds+1Ch ; int
                                   push    dword ptr $JpoJsJzLahfLds+18h ; HDC
                                   call    ds:TextOutA
                                   fldcw   [ebp+var_10]                  ; stack frame dependant !!! 
                                   add     esp, 14h
                                   pop     ebp
                                   popa
                                   retn    14h

                  well, the crucial difference is:


                  Code:
                  pb8:             fldcw   cs:word_42BBF2  ; stack frame independant !!!
                  
                  pb9:             push    133Fh           ; referenced later as [ebp-10]
                                   .
                                   .
                                   .
                                   fldcw   [ebp+var_10]    ; stack frame dependant !!!

                  Summary:

                  the code produced by pb8 is stack frame independant concerning fldcw. As long as
                  i don´t reference local variables in my pb high level code i´m save. This is very
                  nice, because i can write native pb code - not forced to do it all in asm.

                  the code produced by pb9 is (!) stack frame dependant concerning fldcw, because it
                  references [epb-10h], which is a local variable of the current stack frame. But as
                  i´m playing not by the rule here, the stack frame of my replacement routine is not
                  the one pb set up for me. I´m in the stack frame of my host's function. So [ebp-10h]
                  is not where and what it should be. Sometimes this seems to make no difference -
                  sometimes i run into an unexpected exception later on ...



                  Solution:

                  Be a nice guy and setup a stack frame for pb code like this:
                  (don´t forget to clean up afterwards)

                  Code:
                    .
                    .
                    .
                  
                    !  push ebp
                    !  mov ebp, esp
                    !  sub esp, 20
                  
                    !  mov ax, &H133F
                    !  mov [ebp-&H10], ax
                    .
                    .  pb code here ...
                    .
                  
                    !  add esp, 20
                    !  pop ebp

                  I don´t use such dirty tricks all the time, so i can feel very relaxed for all my
                  other code to be compiled with pb9.

                  As far as i can remember this was my first thread i got a reply by Mr. Bob Zale - Wow.
                  I appreciate your work very much !


                  Thanks to all, comments, questions and bugreports (i don´t hope so) appreciated,


                  guenther

                  Comment


                  • #10
                    In one of your code snippets you are calling TextOut() without restoring the register variables. You need to preserve EBX, ESI, EDI, ESP and EBP, and restore them before using any high-level PB statements (such as calling a function or accessing a variable). I'm not 100% but I recall PB8 and less did not use EBX (or it was not mentioned in PB8 help).

                    Also, if you want to handle the stack yourself, do not use "CALL" statement, but ASM CALL.

                    See the help file topic "Saving registers" for more info.
                    kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

                    Comment


                    • #11
                      Kev,


                      that´s why i did a "pushad" before (save registers on the stack) and a
                      "popad" (restore registers) afterwards.

                      Of course i could do an asm call, but the point is: i can do a pb call
                      (not only one - many, as much as i like) here. So i have a generic method
                      of writing my replacement routine in native pb. Comes in very handy, if you
                      have to do heavy string manipulations, math or other things, which are
                      not very handy to code in asm!

                      Pb8 didn´t even reqiure to setup an extra stack frame for this as long as
                      you didn´t reference locals (globals would be ok).

                      Pb9 now requires an extra stack frame to be setup, otherwise you are
                      likely to run into "issues" as described above.

                      I´m aware, that what i´m doing here with my addon is a bit tricky. And it´s
                      been my fault running into trouble. My first guess of different compiler
                      action turned out to be right, but i had absolutly no clue, what to look for.
                      Therefore i started this thread to get ideas just like in a brainstorming
                      session. Reading John´s post it made "click" in my head ...



                      Thanks



                      guenther

                      Comment


                      • #12
                        Of course it's tricky. My point was you shouldn't be messing with the stack if you want your code to be compatible with future versions of PB. If you need to work with the stack when calling a function, then use ASM CALL, it's really not hard to use and you won't encounter problems like this if and when PB change the way the stack is handled. All ASM CALL needs are a few more PUSHes.

                        Just me 2 pence, take it as you will
                        kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

                        Comment


                        • #13
                          Kev,

                          only in my first level (stack frame) i´m in danger, as long as i don´t reconstruct what pb expects in terms of stack.

                          My failure was to rely on the fact pb wouldn´t change its
                          function framework. I don´t see a "compiler update save" way do to
                          what i did here. This is price i have to pay. If the compiler changes
                          the stack framework, i will possibly have to adapt my code.

                          - Well i could do an asm call to a pb routine, pass all necessary parameters
                          and do the real work here. Meaning the first level of my replacement
                          routine would be pure asm - no compiler surprises, no messy stack details.

                          I hope i got your point now and would be glad to here form you, this is the
                          way to go.


                          off topic:
                          as english is not my native tongue, what does

                          Just me 2 pence
                          mean?


                          Thanks for your interesting reply


                          guenther

                          Comment


                          • #14
                            Yes! My advice if you need to make a CALL in the middle of some ASM would be to avoid reading values from PB's stack and handle passing all the parameters yourself. This way there is less room for mistakes

                            A call to a function using ASM is only a handful of instructions (usually only MOV, LEA, PUSH and CALL) and the stack is yours, but remember STDCALL convention uses right-to-left parameters and the called function handles the stack cleanup.

                            PS. It's an American (?) expression "just my 2 cents worth" which basically means "Just my opinion". The "two pence" is my British variation
                            Last edited by Kev Peel; 1 Dec 2008, 06:03 PM.
                            kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

                            Comment


                            • #15
                              Hi all,


                              after some testing, here is the new approach - likely to be more resistant to compiler
                              changes than before. Kev,i was so focused on my approach, that i didn´t see the obvious -
                              and at first i didn´t realize you were in fact one step ahead. Thanks for pointing this
                              out, it is a generic solution now.



                              disassembly of our targets code: (just a sample)

                              Code:
                              .
                              .
                              .
                              00442E70    call textout -> loc_textout = 00442E72 (!)
                              .
                              .
                              .
                              To make things easy, i use globals (in general i don´t mind using globals)
                              Of ourse you have to be part of our target process by beeing an addon, plugin,
                              whatever or by hooking into it...



                              Code:
                              GLOBAL loc_textout as DWORD
                              
                              GLOBAL rc_hdc      AS DWORD
                              GLOBAL rc_x        AS DWORD
                              GLOBAL rc_y        AS DWORD
                              GLOBAL rc_txt      AS ASCIIZ PTR
                              GLOBAL rc_len      AS DWORD
                              
                              
                              .
                              .
                              .
                              
                              
                              
                              
                              SUB paintinit
                              '***********************************************************************************************
                              ' init framework for own textout
                              '***********************************************************************************************
                              local old_protect as long
                              
                              
                                CALL virtualprotect(loc_textout, 16, %Page_Execute_ReadWrite, old_protect)
                              
                                !  mov eax, loc_textout
                                !  lea ebx, dopaint                  'get pointer to replacemant routine for textout and
                                !  mov [eax], ebx                    'stroe at loc_textout
                              
                              
                              EXIT SUB
                              
                              
                              dopaint:                               'replacement routine for textout
                              '! int 3
                              
                                !  mov eax, [esp+4]                  ;retrieve parameters
                                !  mov pt_hdc, eax                   ;get hdc
                                !  mov eax, [esp+8]
                                !  mov pt_x  , eax                   ;get x
                                !  mov eax, [esp+&H0C]
                                !  mov pt_y  , eax                   ;get y
                                !  mov eax, [esp+&H10]
                                !  mov pt_txt, eax                   ;get pointer to text
                                !  mov eax, [esp+&H14]
                                !  mov pt_len, eax                   ;get textlength
                              
                                !  pushad
                              
                                !  lea eax, pbpaint
                                !  call eax                          ;call pb routine
                              
                                !  popad
                                !  ret 20                            ;cleanup stack and return [5 parameters = 20 (decimal)]
                              
                              
                              END SUB
                              
                              
                              
                              SUB pbpaint
                              '***********************************************************************************************
                              ' do all the real work here - in native pb
                              '***********************************************************************************************
                              
                              
                                CALL textout(pt_hdc, pt_x, pt_y, @pt_txt, pt_len)     'draw line
                                .
                                .  (other pb code here)
                                .
                                
                              
                              END SUB

                              You could adapt this sample for different routines with different parameters.


                              Thanks for your help


                              guenther
                              Last edited by Guest; 3 Dec 2008, 09:40 AM. Reason: typos

                              Comment

                              Working...
                              X