I am "playing around" with FPU instructions.
Trying to build a small calculator.
It exists FSQRT to get the square root of the value in ST(0).
But I am breaking my head to get a cubic root.
There is no FPU instruction.
Any ideas ?
Hi,
Quote from: clamicun on March 18, 2017, 04:13:29 AM
Any ideas ?
Look at the exponential function and raise your value to the
one third power.
Cheers,
Steve N.
Thank you Steve,
if you say "function" do you refer to
Logarithmic and exponential instructions ?
F2XM1 2 to the X power Minus 1
FSCALE SCALE ST(0) by ST(1)
FYL2X Y*Log2X
FYL2XP1 Y*Log2(X+1)
Hi,
Yes. Particularly F2XM1. FSCALE uses only integer exponents.
HTH,
Steve
Thanks ... will try it
Example:
include \masm32\MasmBasic\MasmBasic.inc ; download (http://masm32.com/board/index.php?topic=94.0)
Init
fld FP10(0.33333333333333333333333333333)
fld FP10(8.0) ; 2^3
fyl2x
f2xm1
fld1
fadd
Print Str$("The result is %Jf\n", ST(0))
fld FP10(0.33333333333333333333333333333)
fld FP10(1.8816763723536577724902657494247) ; 1.2345^3
fyl2x
f2xm1
fld1
fadd
Print Str$("The result is %Jf\n", ST(0))
EndOfCode
Output:
The result is 2.000000000000000000
The result is 1.234567890123456789
There is a more detailed example Posted on 2003-08-20 12:59:18 by Raymond (http://www.asmcommunity.net/forums/topic/?id=14769), plus a fairly complex function that does not have the limitations of the log functions - see attachment (opens in WordPad in case you don't have RichMasm).
Root of the N-th power of the number
fld Exponent
fld st ;st=st(1)
fabs ;st=|exp|
fld Max
fcompp ;leave exp in st(0)
fstsw ax
sahf
jb @@RealPower ;exp > MaxInt?
fld st ;exp=st(0)=st(1)
frndint ;round(exp)
fcomp ;compare exp and round(exp)
fstsw ax
sahf
jne @@RealPower
fistp IntExp
mov eax, IntExp ;eax=Trunc(Exponent)
mov ecx, eax
cdq
fld1 ;st(0)=1
xor eax, edx
sub eax, edx ;eax=|exp|
jz @@Exit
fld Base
jmp @@Entry
@@Loop:fmul st, st ;st(0)=Base^2
@@Entry:shr eax, 1
jnc @@Loop
fmul st(1), st ;Result * X
jnz @@Loop
fstp st
cmp ecx, 0
jge @@Exit
fld1
fdivrp ;st(0)=1/Result
jmp @@Exit
@@RealPower:fld Base
ftst
fstsw ax
sahf
jz @@Done
fldln2
fxch
fyl2x
fxch
fmulp st(1), st
fldl2e
fmulp st(1), st
fld st(0)
frndint
fsub st(1), st
fxch st(1)
f2xm1
fld1
faddp st(1), st
fscale
@@Done:fstp st(1)
@@Exit:
JJ,
fld FP10(8.0) ; 2^3 ok.
If I change this to e.g.
fld FP10(27.0) ; 3^3 not ok.
fld FP10(64.0) ; 4^3 not ok.
??
If you have the MASM32 FDK, you can use the FpuXexpY function of the provided Fpu.lib library, with 1/3 as the exponent. Or, if you want to code the FPU yourself, you can always look at the source code of that function and adapt it to your own specific needs.
Mikl__
.data ???
Guten Abend, clamicun!
Wählen Sie die Daten auf eigene Faust. Das Programm berechnet nicht nur die Wurzel des Würfels
Mikl__ Danke,
aber was ist der "Input" ? --- also der Wert aus dem die 3Wurzel berechnet werden soll --- Max ?
intExp ??
base ??
Quote from: clamicun on March 19, 2017, 02:26:48 AM
JJ,
fld FP10(8.0) ; 2^3 ok.
If I change this to e.g.
fld FP10(27.0) ; 3^3 not ok.
fld FP10(64.0) ; 4^3 not ok.
??
Quote from: jj2007 on March 18, 2017, 12:02:36 PMThere is a more detailed example Posted on 2003-08-20 12:59:18 by Raymond (http://www.asmcommunity.net/forums/topic/?id=14769), plus a fairly complex function that does not have the limitations of the log functions
There is no easy way out :bgrin:
JJ,
"CubeLogA" from Raymond works perfekt with integers ...
wonder how floats are working.
; GUI #
include win64a.inc
include msvcrt.inc
includelib msvcrt.lib
.data
n dd 3.0,3.0,3.0,3.0;exponent
y dd 125.0,?,?,?;number
x dd ?,?,?,?;result
.code
WinMain proc
sub esp,28h
movups xmm0,y
rcpps xmm1,n ;xmm1=1/exponent
invoke powf;float powf(float x, float y);Calculates x raised to the power of y
movups x,xmm0;x=y^{1/n}
fld x
fistp x
mov eax,x
invoke ExitProcess,0
WinMain endp
end
Thanks to all.
I think for my small calculator Raymonds function is the best.
It is not the "Windows calculator" but does the job.
;====================
.data
recip3 QWORD 0.33333333333333333333333333333
value1 db 16 dup(?),0
FPU_cubic_root proc
finit
FFREE st(0)
FFREE st(1)
fld recip3
INVOKE crt_atof,offset value1 ;value1 = Input string from a dialogbox
;st(0)=value1, st1=recip3
fabs
fyl2x ;->log2(Src1)*exponent
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
fscale ;scale it with the characteristic
fstp st(1) ;copy result over and "pop" it
ret
FPU_cubic_root endp
;====================
i highly recommend using 80-bit reals with the FPU
if you want a smaller result at the end, fine - the FPU will convert for you
to define data...
MyReal REAL10 1.234567890123456789
clamicun,
schauen Sie hier http://www.asmcommunity.net/forums/topic/?id=14769
clamicun
Quotefinit
FFREE st(0)
FFREE st(1)
fld recip3
Just a few comments to prevent you from making future mistakes.
i) The
finit instruction reinitializes
all the fpu registers (and also sets the precision control to its full 80 bits). The
ffree instructions in your posted code are thus useless at that point.
ii) Even if you didn't use the finit instruction in your posted code, the
ffree instructions would also have been useless at that point. The reason is that you cannot load a value into an fpu register if it is not free. AND the fld instruction would attempt to load a value into what would be the current
st(7) register. (This is the type of pitfall that programmers fall into by trying to use the fpu without sufficient prior knowledge).
You may want to have a look at the following tutorial, at least of the first few chapters.
http://www.ray.masmcode.com/tutorial/index.html (ftp://www.ray.masmcode.com/tutorial/index.html)
Thank you Raymond,
"trying to use the fpu without sufficient prior knowledge"
Might depend on someones type...
I always learnt by doing and trying.
Quote from: clamicun on March 20, 2017, 04:18:36 AMI always learnt by doing and trying.
That won't work with the FPU :biggrin:
Ray's link above points to a FTP site. Here is the correct one. (http://www.ray.masmcode.com/tutorial/index.html) See in particular the "revolver" figure in chapter 1.
If you don't want to launch the debugger, the deb (http://www.webalice.it/jj2006/MasmBasicQuickReference.htm#Mb1019) macro is a good alternative; it displays ST(0)...ST(5) - 6+7 are zero because the macro itself needs the FPU:
include \masm32\MasmBasic\MasmBasic.inc
Init
FpuFill ; fill the FPU with 1001 ... 1008
deb 4, "FPU filled", ST(0), ST(1), ST(2), ST(3), ST(4), ST(5), ST(6), ST(7) ; 6+7 not visible!
fldpi ; try to put PI into ST(0) - will be zero, and that is an error
deb 4, "FPU: fldpi", ST(0), ST(1), ST(2), ST(3), ST(4), ST(5), ST(6), ST(7)
ffree st(7)
fldpi ; put PI into ST(0) - the zero will move to ST1
deb 4, "FPU: ffree7 + fldpi", ST(0), ST(1), ST(2), ST(3), ST(4), ST(5), ST(6), ST(7)
Inkey "OK?"
EndOfCode
Output:
FPU filled
ST(0) 1001.000000000000000
ST(1) 1002.000000000000000
ST(2) 1003.000000000000000
ST(3) 1004.000000000000000
ST(4) 1005.000000000000000
ST(5) 1006.000000000000000
ST(6) 0.0
ST(7) 0.0
FPU: fldpi
ST(0) 0.0 <<<<<<<<<<< you would expect 3.14159 here, but ST(7) was not free...!
ST(1) 1001.000000000000000
ST(2) 1002.000000000000000
ST(3) 1003.000000000000000
ST(4) 1004.000000000000000
ST(5) 1005.000000000000000
ST(6) 0.0 <<<<<<< can't be displayed, but it is 1006.0 indeed
ST(7) 0.0 <<<<<<< can't be displayed, but it is 1007.0 indeed
FPU: ffree7 + fldpi
ST(0) 3.141592653589793238
ST(1) 0.0
ST(2) 1001.000000000000000
ST(3) 1002.000000000000000
ST(4) 1003.000000000000000
ST(5) 1004.000000000000000
ST(6) 0.0
ST(7) 0.0
QuoteI always learnt by doing and trying.
Would that have been the way you learned trigonometry, or calculus, or simply extracting square roots, or ... :biggrin: :biggrin: :biggrin: :dazzled:
Just kidding.
after giving it a little more thought....
a nice way to go might be to write a general-purpose function (i.e., PROC) that will do something like
ST(0) = ST(0)^ST(1)
let's call that one ExpReal
you can then write other "wrapper" functions to call that one
one such function might load the value 1/3 (REAL10) and the user's REAL8, then call ExpReal and return a REAL8 result
the function would then insure FPU register balance before exit
the ExpReal function can be used in many ways, on any size data
Hi, dedndave!
I do not even need to invent anything, in http://masm32.com/board/index.php?topic=6075.msg64353#msg64353 I use function float powf(float x, float y) from msvcrt.dll. There it is written for the masm64 -- probably therefore and it was not appreciated
i wouldn't use the MSVCRT unless i had to - lol
that's your choice
this could be turned into a macro, i suppose - or a PROC
as i recall, i based this on some code by Raymond and/or Marinus
;raising the power of x to the y is performed using logarithms:
;x^y = alog(y*log(x)), where x is any positive real
;the log and alog functions may use any base, so long as they are both the same (the FPU natively uses base 2)
;
;FYL2X (float y*log base 2 of x) performs the y*log(x) portion of the above equation
;(FYL2X is also commonly used for converting logarithms between bases)
;
;code to compute the y*log2(x) portion, if both operands are REAL numbers
;in memory and no error checking is required would be as follows.
;ST(0)=zzz
fld exponent_y ;load the exponent first
;->ST(0)=exponent, ST(1)=zzz
fld number_x ;then load the x value
;->ST(0)=x value, ST(1)=exponent, ST(2)=zzz
fyl2x ;ST(0)=y*log2(x), ST(1)=zzz
;the alog (antilog) portion is performed using the following FPU logarithm instructions:
;FRNDINT (float round to integer)
;F2XM1 (float (2 to the x) minus 1)
;FSCALE (float scale ST(0) by ST(1))
fld st ;make a second copy
;ST(0)=y*log2(x), ST(1)=y*log2(x), ST(2)=zzz
frndint ;round it to an integer
;ST(0)=int[y*log2(x)], ST(1)=y*log2(x), ST(2)=zzz
fsub st(1),st ;this will leave only a fractional portion in ST(1)
;ST(0)=int[y*log2(x)], ST(1)=y*log2(x)-int[y*log2(x)], ST(2)=zzz
fxch st(1) ;ST(0)=y*log2(x)-int[y*log2(x)], ST(1)=int[y*log2(x)], ST(2)=zzz
f2xm1 ;get the fractional power of 2 (minus 1)
;ST(0)=2^(ST(0))-1, ST(1)=int[y*log2(x)], ST(2)=zzz
fld1 ;ST(0)=1, ST(1)=2^(ST(0))-1, ST(2)=int[y*log2(x)], ST(3)=zzz
fadd ;add the 1 to ST(1) and POP ST(0)
;ST(0)=2^(ST(0)), ST(1)=int[y*log2(x)], ST(2)=zzz
fscale ;add the integer in ST(1) to the exponent of ST(0)
;effectively multiplying the content of ST(0) by 2^int
;and yielding the final result of x^y
;ST(0)=x^y, ST(1)=int[y*log2(x)], ST(2)=zzz
fstp st(1) ;the content of ST(1) has become useless
;overwrite the content of ST(1) with the result and POP ST(0)
;ST(0)=x^y, ST(1)=zzz
fstp result_z ;ST(0)->result
;ST(0)=zzz
I used the Randy Hyde's pow macro for some years (since 2000): TwoToX macro
fstcw SavedCW
fstcw MaskeCW
or byte ptr MaskeCW+1, 1100b
fldcw MaskeCW
fld st(0)
fld st(0)
Frndint
Fxch
Fsub st(0) , st(1)
F2xm1
Fld1
Fadd
Fxch
Fld1
Fscale
Fstp st(1)
fmul
Fstp st(1)
fldcw SavedCW
endm
pow MACRO M1, M2 ; M1 ^ M2
Fld M1
Fld M2
Fxch
Fld1
Fxch
Fyl2x
Fmul
TwoToX
ENDM
; Eleva a M2 la que está en st(0)
; ej: 2^10
; fld diez
; pow1 dos
pow1 MACRO M2 ; M1 ^ M2
Fld M2
Fxch
Fld1
Fxch
Fyl2x
Fmul
TwoToX
ENDM
; ej: 2^10
; fld diez
; fld dos
; pows
pows MACRO ; st (1) ^ st(0)
Fxch ; primero cargar el numero y despues el exponente
Fld1
Fxch
Fyl2x
Fmul
TwoToX
ENDM
and because work perfectly, I don't remember how it works. :biggrin:
Expreal MACRO
fyl2x
fld st
frndint
fsub st(1),st
fxch st(1)
f2xm1
fld1
fadd
fscale
fstp st(1)
ENDM
;usage....
fld exponent_y
fld number_x
Expreal
fstp result_w ;w = x^y