This is a fast, extended-precision cube root function which may be be useful to PB programmers who require such a thing.
Just save the code to a file and include it in your source header.
To invoke the function in a PB procedure use x3rdR(numeric expression).
If you don't like the function name, use a macro to change it. I wont be offended.
Any questions or bug reports, please message me.
Just save the code to a file and include it in your source header.
To invoke the function in a PB procedure use x3rdR(numeric expression).
If you don't like the function name, use a macro to change it. I wont be offended.
Any questions or bug reports, please message me.
Code:
'x3rdR include file "FPx3rdR.inc" #IF NOT %DEF(%x3rdR_I) MACRO FUNCTION x3rdR(x) 'x may be a numeric variable or expression mGxfA = x !call FPX3rdR END MACRO = mGxfA FASTPROC FPX3rdR() !mov ax, mGxfA[8] ;ax = sb(x) + biased exponent of x !test ax, ax !jz JZ_ ;exit if x = 0 'set exponent of x to zero and sign of x to + !mov ecx, &h00003fff ;cx = exponent bias !mov mGxfA[8], cx ;mGxfA = ABS(Significand of x) !fld mGxfA ;st(0) = S 'Newton's-method constants for cube roots !fld tbyte ADC_xfOneThird ;st(0) = 1/3, st(1) = S !fmul st(1), st(0) ;st(0) = 1/3, st(1) = S/3 !fadd st(0), st(0) ;st(0) = 2/3, st(1) = S/3 'First approximation, A = S/3 + 2/3 max error is 5.83% at S = 1.999... !fld st(0) ;st(0) = 2/3, st(1) = 2/3, st(2) = S/3 !fadd st(0), st(2) ;st(0) = A, st(1) = 2/3, st(2) = S/3 '1st iteration of NM !fld st(0) ;st(0) = A, st(1) = A, st(2) = 2/3, st(3) = S/3 !fmul st(0), st(2) ;st(0) = 2*A/3, st(1) = A, st(2) = 2/3, st(3) = S/3 !fxch st(1) ;st(0) <-> st(1) --- st(0) = A, st(1) = 2*A/3, st(2) = 2/3, st(3) = S/3 !fmul st(0), st(0) ;st(0) = A^2, st(1) = 2*A/3, st(2) = 2/3, st(3) = S/3 !fdivr st(0), st(3) ;st(0) = S/(3*A^2), st(1) = 2*A/3, st(2) = 2/3, st(3) = S/3 !xor ebx, ebx ;ebx = 0 !btr ax, 15 ;cf = sb(x): sb(ax) = 0 !rcr bx, 1 ;sb(bx) = sb(x) !faddp st(1), st(0) ;st(0) = revised approx of S^(1/3) = 2*A/3 + S/(3*A^2), st(1) = 2/3, st(2) = S/3 '2nd iteration !fld st(0) !fmul st(0), st(2) !fxch st(1) !fmul st(0), st(0) !fdivr st(0), st(3) !sub ax, cx ;ax = exponent of x in 2's complement (E) !movsx eax, ax ;eax = E !mov edx, &H55555556 ;edx ~= (2^32)/3 !faddp st(1), st(0) '3rd !fld st(0) !fmul st(0), st(2) !fxch st(1) !fmul st(0), st(0) !fdivr st(0), st(3) !imul edx ;edx = INT(E/3) !faddp st(1), st(0) '4th !fld st(0) !fmul st(0), st(2) !fxch st(1) !fmul st(0), st(0) !fdivr st(0), st(3) !add eax, &h00010000 ;results in cf = 1 if eax = &hffff???? !adc edx, ecx ;edx = edx + &h3fff + carry from previous add !or edx, ebx ;sb(edx) = sb(x) !shr eax, 24 ;see below at R01 !faddp st(1), st(0) 'discard NM constants 2/3 and S/3 !fstp st(1) !fstp st(1) ;st(0) ~= S^(1/3) R01: !jz J02 ;zero means no remainder (2^0 = 1), so mult at J01 is redundant !fld tbyte ptr ADC_3rdR_xf2toOneThird ;st(0) = 2^(1/3), st(1) ~= S^(1/3) !sub eax, &h0000055 ;for &h55 factor is 2^(1/3); for &hAA, 2^(2/3) !jz J01 !fmul st(0), st(0) ;st(0) = 2^(2/3), st(1) = S^(1/3) J01: !fmulp st(1), st(0) ;st(0) = S^(1/3) * 2^([1|2]/3) J02: !fstp mGxfA ;store this part of result in return variable !mov mGxfA[8], dx ;exponent of answer = INT(E/3) + exponent of st(0) at J02 JZ_: 'Exit point for x = 0 END FASTPROC '*** RFT for 3rdroot #ALIGN 16 ASMDATA ADC_xfOneThird DW &HAAAB, &HAAAA, &HAAAA, &HAAAA, &H3FFD, 0, 0, 0 '1/3 END ASMDATA ASMDATA ADC_3rdR_xf2toOneThird DW &H5712, &H6B94, &H17CC, &HA145, &H3FFF '2^(1/3) END ASMDATA #IF NOT %DEF(%mGVars) #ALIGN 16 GLOBAL mGxfA AS EXT GLOBAL mGInt AS INTEGER GLOBAL mGLInt AS LONG GLOBAL mGxfEst AS EXT %mGVars = -1 #ENDIF %x3rdR_I = -1 #ENDIF 'end x3rdR include file "FPx3rdR.inc"
Comment