News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

cubic root

Started by clamicun, March 18, 2017, 04:13:29 AM

Previous topic - Next topic

clamicun

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
;====================

dedndave

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

Mikl__

clamicun,
schauen Sie hier http://www.asmcommunity.net/forums/topic/?id=14769

raymond

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
Whenever you assume something, you risk being wrong half the time.
https://masm32.com/masmcode/rayfil/index.html

clamicun

Thank you Raymond,

"trying to use the fpu without sufficient prior knowledge"
Might depend on someones type...
I always learnt by doing and trying. 

jj2007

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. See in particular the "revolver" figure in chapter 1.

If you don't want to launch the debugger, the deb 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

raymond

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.
Whenever you assume something, you risk being wrong half the time.
https://masm32.com/masmcode/rayfil/index.html

dedndave

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

Mikl__

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

dedndave

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

HSE

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:
Equations in Assembly: SmplMath

dedndave

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