News:

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

FPU - How to test for infinity

Started by RuiLoureiro, May 23, 2012, 09:13:40 PM

Previous topic - Next topic

RuiLoureiro

Hi,
        After some operations
        when i retrieve exception flags from FPU
        it gives me invalid operation.
        I want to know if it is infinity
        or another case. Can i know the case ?
        How to do that ?
       
        In this particular case the factors are
        X=3 and Y=11623334155587.786 and it seems
        that the result is infinity
       
        Thanks
       
Quote
    fstsw ax                ;retrieve exception flags from FPU
    fwait
    shr   al,1              ;test for invalid operation
    jc    short _error

raymond

From the description of the Status Word,
QuoteBits 6-0 are flags raised by the FPU whenever it detects an exception. Those exception flags are cumulative in the sense that, once set (bit=1), they are not reset (bit=0) by the result of a subsequent instruction which, by itself, would not have raised that flag. Those flags can only be reset by either initializing the FPU (FINIT instruction) or by explicitly clearing those flags (FCLEX instruction).

Which means that if you check for an invalid operation at the end of a series of FPU instructions, that flag could have been raised by any of those previous instructions and not necessarily by the last one. When you need to find where the invalid operation occurs, you need to check for it after each operation which could be the culprit. If you are not entirely sure if an instruction could be the source of an invalid operation, you will have to verify its description; there are too many of those to list here.

A result of infinity would cause an Overflow exception but not always raise the Invalid Operation flag. Dividing by 0 would also raise the Divide-by-zero flag but not the Invalid Operation flag unless the value you are trying to divide is also 0.
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

RuiLoureiro

raymond,
        Thank you for reply

Quote
Which means that if you check for an invalid operation at the end of a series
of FPU instructions
, that flag could have been raised by any of those
previous instructions and not necessarily by the last one.

        No, i test each procedure that completes one operation.
        We must assume that each procedure tests each result
        in such a way that it is not possible to go to the
        next one with Invalid Operation or Overflow.

               
Quote
A result of infinity would cause an Overflow exception but not always
raise the Invalid Operation flag.

        Here is the question.
        When we have a lot of functions to process we need to have a good model.

        It seems that we need to test
       
        first the Invalid Operation flag
                If it is an Invalid Operation, then
        second  we test the Overflow exception.
       
        So one model is this:

Quote
            fstsw ax                ;retrieve exception flags from FPU
            fwait
            shr   al,1              ;test for invalid operation
            jc    short _erro
            ;
            ; Ok, Go on
            ; ---------
   
            ...
            clc
            ret
;---------------   
_erro:      test    ax, 0000100b
            jnz     _overflow

            mov     _ErrorZ, 1
            stc
            ret

_overflow:  mov     _ErrorZ, 2
            stc
            ret

        I have one variable where i put the error code.
       
        It is _ErrorZ which starts with 0.
        _ErrorZ=0 means "undefined error".
        All my procs exit with carry clear if no error
        or carry set if error.
        When the first procedure i call to solve an expression
        exits we have 2 cases:
       
            clc:    we have the correct result
            stc:    we have the error code in _ErrorZ

        One thing to you
         
        I learned a lot of things with your FPU lib.
        As far as i know you never test overflow
        but only "invalid operation".
       
        In this particular problem it has something to do with
        FpuXexpY. In fact, the problem is when i compute
        XexpY. In your procedure you wrote this:

Quote
dest0:
      fxch                    ;set up FPU registers for next operation
      fyl2x                   ;->log2(Src1)*exponent
     
;the FPU can compute the antilog only with the mantissa
;the characteristic of the logarithm must thus be removed
     
      fld   st(0)             ;copy the logarithm
      frndint                 ;keep only the characteristic
      fsub  st(1),st          ;keeps only the mantissa
      fxch                    ;get the mantissa on top

      f2xm1                   ;->2^(mantissa)-1
      fld1
      fadd                    ;add 1 back

;the number must now be readjusted for the characteristic of the logarithm

      fscale                  ;scale it with the characteristic     
      fstsw ax                ;retrieve exception flags from FPU
      fwait
      shr   al,1              ;test for invalid operation
      jc    srcerr            ;clean-up and return error

        Now, i tested, and after fscale i got
        Overflow exception. And i thought why raymond didnt do
        it ? I dont know if you want to do it or not but
        i think the lib functions should exit with 2 error
        codes: one for "Overflow" and the other to "invalid operation"
        which means "undefined error". This 2 cases are not the
        same thing. But ok, you do what you want to do.
        It's all.

        Ok, thank you  :U

raymond

QuoteIn this particular case the factors are
X=3 and Y=11623334155587.786

Just curious. Where you trying to compute 311623334155587.786 or 11623334155587.7863?
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

RuiLoureiro

Quote
Just curious
            Try to explain how do you write a procedure to compute
                   real1^real2^real3^...^realN

            I want to know ! May be you test the exponent first
            to see if it is 11623334155587.786 !  :P
        Dont forget i wrote this also:

        «After some operations
        when i retrieve exception flags from FPU
        it gives me invalid operation.»

raymond

Quotereal1^real2^real3^...^realN

Even that is ambiguous unless you use brackets. For example, 4^3^2 could be interpreted as either (4^3)^2=46 or 4^(3^2)=49

And its obvious that if your exponent is large enough, you would get overflow but not necessarily an invalid operation at that point. You may get an invalid operation if you try to reuse the INFINITY value with other FPU instructions.

If you rely on the Fpulib to perform all your floating point computations, there is a function (FpuExam) to test if a parameter is effectively INFINITY if you have any doubt before proceeding with further computations.
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

RuiLoureiro

Quote
Even that is ambiguous unless you use brackets. For example, 4^3^2 could be interpreted
as either (4^3)^2=46 or 4^(3^2)=49
            It is not ambiguous ONLY because the calculator follows this rule:
            First compute the last, or starts by the end.
            So in the case 4^3^2: first 3^2=x, then 4^x.

Quote
you would get overflow but not necessarily an invalid operation at that point
            Yes it is the same you told us in the previous post:
                         
            «A result of infinity would cause an Overflow exception but not always
            raise the Invalid Operation flag»

            I dont want to test Overflow first. Based on my example i want
            to test Invalid Operation first. If is invalid i test Overflow.

            Well if it is Overflow we get the message: Infinity;
            If not we get simply ERROR.

Quote
You may get an invalid operation if you try to reuse the INFINITY value with other
FPU instructions.
            If i get the overflow first it does not go to this case

Quote
If you rely on the Fpulib to perform all your floating point computations...

            I am not using Fpulib but it is a reference

Quote
there is a function (FpuExam) to test if a parameter is effectively INFINITY
if you have any doubt before proceeding with further computations.

            Could you explain why to use FpuExam after i get
            first:      Invalid Operation
            and second: Overflow ?
            If i get Overflow, i need to use FpuExam ?
            Is it necessary ? We cannot assume it is INFINITY ?           

RuiLoureiro

Well (based on what you wrote) i used WhatCaseIsIt to see
what case is it after getting Invalid Operation and after
getting Overflow.
The result is this: positive Infinity !
So we dont need to use fxam.

If you want know what is the expression i used in the calculator
to get this case, it is: 2^3^log(2)^-e^+pi

log(2)^-e^+pi = 1162334155587.786

Quote
WhatCaseIsIt    proc

                fxam           ;examine it
                fstsw ax       ;copy the content of the Status Word to AX
                fwait          ;insure the last instruction is completed
                sahf           ;copy the C3/C2/C0 condition codes to the ZF/PF/CF flags
                jz    C3is1    ;either Zero, Empty or Denormalized if C3=1
                jpe   C2is1    ;either normal or infinity if C3=0 and C2=1
                jc    isNAN   
                ;
                fn      MessageBox, hDlg, "Dont know", "WhatCaseIsIt", MB_OK               
                ret
               
                ; code for the case of a NAN, no need to check the sign
    isNAN:
                fn      MessageBox, hDlg, "NAN", "WhatCaseIsIt", MB_OK
                ret
               
                ; would be Infinity if C3=0, C2=1 and C0=1
    C2is1:     
                jc    isINFINITY ;would be Infinity if C3=0, C2=1 and C0=1
                                 ;this leaves the case for a Normal finite number
                test  ah,2       ;test for the sign which is in bit1 of AH
                jnz   negNORMAL
                ;code for the case of a positive Normal finite number
               
                fn      MessageBox, hDlg, "positive Normal finite number", "WhatCaseIsIt", MB_OK               
                ret

                ; code for the case of a negative Normal finite number
    negNORMAL:

                fn      MessageBox, hDlg, "negative Normal finite number", "WhatCaseIsIt", MB_OK
                ret
    isINFINITY:
                test  ah,2     ;test for the sign which is in bit1 of AH
                jnz   negINFINITY
                ; code for the case of a positive Infinity

                fn      MessageBox, hDlg, "positive Infinity", "WhatCaseIsIt", MB_OK

                ret
    negINFINITY:
                ;code for the case of a negative Infinity
                fn      MessageBox, hDlg, "negative Infinity", "WhatCaseIsIt", MB_OK

                ret
                ; ------------------
                ; Stop HERE with ret
                ; ------------------
    C3is1:                   
                jc    isEMPTY  ;would be Empty if C3=1 and C0=1
                jpe   isDENORMAL ;would be a Denormalized number if C3=1, C0=0 and C2=1
               
                  ;this leaves the case for a Zero value
                ;code for the case of a Zero value, no need to check sign
                fn      MessageBox, hDlg, "Zero value", "WhatCaseIsIt", MB_OK
                ret
    isEMPTY:
                ;code for the case of an Empty register
                            ;which does not apply in this example because
                            ;ST(0) was loaded with a value from memory

                fn      MessageBox, hDlg, "Empty register", "WhatCaseIsIt", MB_OK
                ret
    isDENORMAL:
                test ah,2     ;test for the sign which is in bit1 of AH
                jnz   negDENORMAL
               
                ;code for the case of a positive Denormalized number
                fn      MessageBox, hDlg, "positive Denormalized number", "WhatCaseIsIt", MB_OK
                ret
    negDENORMAL:
                ;code for the case of a negative Denormalized number
                fn      MessageBox, hDlg, "negative Denormalized number", "WhatCaseIsIt", MB_OK
                ret
WhatCaseIsIt    endp

jj2007

Quote from: raymond on May 24, 2012, 01:02:24 AM
From the description of the Status Word,
QuoteBits 6-0 are flags raised by the FPU whenever it detects an exception. Those exception flags are cumulative in the sense that, once set (bit=1), they are not reset (bit=0) by the result of a subsequent instruction which, by itself, would not have raised that flag. Those flags can only be reset by either initializing the FPU (FINIT instruction) or by explicitly clearing those flags (FCLEX instruction).

Ray,
I am trying to raise an exception, but my FPU seems to like the little shock of dividing by zero - it just continues.
Olly says the mask bits are all cleared, so in principle it should trigger the exception. Is there any such CPU-specific behaviour? I have a Celeron M here...
Thanks for advice,
Jochen

include \masm32\include\masm32rt.inc

Near64 = 10000011b

.code
start: finit
push Near64*256
fldcw [esp] ; clear exception masks
pop eax
fldpi
fldz
fdiv ; 3.1415/0 -> should trigger exception
inkey "ok"
exit

end start


P.S.: I wonder if it is a hardware problem:
QuoteI had some old style PCs about 8 years ago that did not have the FPU interrupt correctly connected to the proper BIOS' interrupt handler that would do a BSOD.  In the fast modern PCs, there is a completely different firmware software solution to catching the FPU exceptions which is controlled by a bit in CPU register CR0

raymond

Quote2^3^log(2)^-e^+pi

So you first get 3^log(2)^-e^+pi = INFINITY which raises the Overflow flag but not the Invalid Operation flag.
However, the next operation would then become 2^INFINITY, and THAT raises the Invalid Operation flag.

Remember what I mentioned before?
QuoteYou may get an invalid operation if you try to reuse the INFINITY value with other FPU instructions.

If the Invalid Operation flag is raised at the END of a series of operations, I would suggest that the return answer should be INVALID (or UNDEFINED) regardless if the Overflow flag is raised. You should have a similar outcome as your example with the following example, but with the Divide-by-Zero flag being raised instead of the Overflow flag:
2^(3/(log(10)-1)) = 2^INFINITY

From a strictly math point of view, the actual result of your example is NOT infinity although it may be a very VERY VERY large value. Only my example could be considered as being equal to infinity (without the Overflow flag being raised).
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

raymond

Jochen,

You must mean you were trying to get the FPU to generate an interrupt whenever a particular exception is detected.

From my tutorial, in the description of the interupt masks of the Control Word,
QuoteThis document will not describe how interrupts are generated and transmitted nor how to respond to such interrupts.

I haven't yet delved into that subject myself. Maybe I should in the near future, if only as an addendum to the tutorial.
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

MichaelW

This is not very well worked out or tested, and I didn't get to the infinity test, but it's a start.
eschew obfuscation

RuiLoureiro

#12
Well i think you are thinking as if your procedure do the task and you are wrong.

    1. This statement is wrong
Quote
So you first get 3^log(2)^-e^+pi = INFINITY which raises the Overflow flag
but not the Invalid Operation flag.

       We should say:
Quote
We compute log(2)^-e^+pi and we get 1162334155587.786
Then, in the next step, when we try to compute 3^1162334155587.786
it raises the Invalid Operation flag
AND the Overflow flag is also set

    2. This statement is wrong
Quote
However, the next operation would then become 2^INFINITY,
and THAT raises the Invalid Operation flag.

    We should say simply   
Quote
    We have not NEXT OPERATION

    Try to understand that i use this basic rule:
        When i get "Invalid Operation" or "Overflow"
        all computations are stoped

    3.  We are talking about FPU
Quote
From a strictly math point of view, the actual result of your example is ...

        Well we need a statement like this:

Quote
From a strictly FPU point of view, the actual result of your example ...???

    4.   
Quote
You should have a similar outcome as your example with the following example,
but with the Divide-by-Zero flag being raised instead of the Overflow flag:
2^(3/(log(10)-1)) = 2^INFINITY
        See point 2.
        From a strictly FPU point of view i want to say that i dont know
        what is the result of INFINITY * 0  or INFINITY/INFINITY
        0/INFINITY etc.

Jochen,
Quote
Olly says the mask bits are all cleared, so in principle it should trigger the exception
        I dont like to follow this way, so dont know why.

EDIT: If you want, i can post the procedure that does it and you
      can analyse it

jj2007

Quote from: MichaelW on May 25, 2012, 04:56:30 AM
This is not very well worked out or tested, and I didn't get to the infinity test, but it's a start.

Thanks, Michael. When stepping through your code and comparing it to mine, I finally found the reason why mine didn't trigger the exception: because fdiv memzero only sets the zerodivide exception flag - the exception is triggered at the next FPU instruction (and my code had no further FPU instructions).
A simple fstp st is sufficient.

Again, thanks for putting this together - one more lesson learnt :bg

RuiLoureiro

I am trying to see why i get Invalid Operation AND the Overflow flag is set

Previous computation ? No. I call one procedure
but it doesnt set the overflow flag.

And, with or without FCLEX at the end of that procedure
"i get Invalid Operation AND the Overflow flag is set"