assemblyx87

Clamping the results of 1-(x/y) to -1 .. +1 range for positive inputs, and special casing zero


I have two problems to solve in x87 ASM and I'm a little confused by it, could anyone suggest some ideas?

Equation: 1-(x/y)

Background: X and Y are always positive floating point numbers, but the result of the calculation can be either positive or negative. This is part of a wider calculation which always results in a positive number.

Problem 1: Either X or Y can be zero (and both can be zero simultaneously). Divide by zero is bad. However if either value is zero, I want the final result to be "1" (i.e. Fld1, pop any unnecessary numbers and then move on)

Problem 2: The result must always fall between -1 and +1. If the result is above 1 or below -1, I just want to replace the result with 1. (I.e. In the case of 1-(35/0.01).)

So far I have the following:

fld dword [y] ; Check if either number is 0
fmul dword [x] ; 
Fldz
fcomip st(1),st(0)
je exit_1  ; If result is zero jump outta here 
fld dword [y] ; Otherwise load the floats
fld dword [x]
fdivp        ; Do X/Y
fld1        
fsubrp       ; 1 - X/Y
????? ; Need some code here to check result is between -1 and 1. If not, replace the result with "1" every time. 

.exit_1
fld1 ; Use 1 as the result for the next calculation 
jump to_next_calc

Solution

  • In order to the value of 1 - x/y to go below -1, x/y > 2, meaning that you can early exit with

       if (x >= (y + y)) return -1.0f;
    

    Having = also handles the case of x==0 and y==0, where as the > handles the case of x>0 and y==0.

    Then proceed normally and the output is limited to [-1 .. 1] automatically.

    The multiplication of y by two can be theoretically speeded up by adding 1 to the exponent - given that the number is not denormal, inf, or nan, and does not overflow...