math.inc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2003 by the Free Pascal development team.
  4. Implementation of mathematical Routines (for extended type)
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {****************************************************************************
  12. FPU Control word
  13. ****************************************************************************}
  14. procedure Set8087CW(cw:word);
  15. begin
  16. { pic-safe ; cw will not be a regvar because it's accessed from }
  17. { assembler }
  18. default8087cw:=cw;
  19. asm
  20. fnclex
  21. fldcw cw
  22. end;
  23. end;
  24. function Get8087CW:word;assembler;
  25. asm
  26. push ax
  27. mov bx, sp
  28. fnstcw word ptr ss:[bx]
  29. pop ax
  30. end;
  31. procedure Handle_I8086_Error(InterruptNumber : dword); public name 'FPC_HANDLE_I8086_ERROR';
  32. var
  33. FpuStatus : word;
  34. OutError : dword;
  35. begin
  36. OutError:=InterruptNumber;
  37. case InterruptNumber of
  38. 0 : OutError:=200; {'Division by Zero'}
  39. 5 : OutError:=201; {'Bounds Check', not caught yet }
  40. 12 : OutError:=202; {'Stack Fault', not caught yet }
  41. 7, {'Coprocessor not available', not caught yet }
  42. 9, {'Coprocessor overrun', not caught yet }
  43. 16,$75 :
  44. begin
  45. { This needs special handling }
  46. { to discriminate between 205,206 and 207 }
  47. asm
  48. fnstsw fpustatus { This for is available for 8086 already }
  49. fnclex
  50. end;
  51. if (FpuStatus and FPU_Invalid)<>0 then
  52. OutError:=216
  53. else if (FpuStatus and FPU_Denormal)<>0 then
  54. OutError:=216
  55. else if (FpuStatus and FPU_DivisionByZero)<>0 then
  56. OutError:=200
  57. else if (FpuStatus and FPU_Overflow)<>0 then
  58. OutError:=205
  59. else if (FpuStatus and FPU_Underflow)<>0 then
  60. OutError:=206
  61. else
  62. OutError:=207; {'Coprocessor Error'}
  63. { if exceptions then Reset FPU and reload control word }
  64. if (FPUStatus and FPU_ExceptionMask)<>0 then
  65. SysResetFPU;
  66. end;
  67. end;
  68. HandleError(OutError);
  69. end;
  70. {****************************************************************************
  71. EXTENDED data type routines
  72. ****************************************************************************}
  73. {$define FPC_SYSTEM_HAS_ABS}
  74. function fpc_abs_real(d : ValReal) : ValReal;compilerproc;
  75. begin
  76. { Function is handled internal in the compiler }
  77. runerror(207);
  78. result:=0;
  79. end;
  80. {$define FPC_SYSTEM_HAS_SQR}
  81. function fpc_sqr_real(d : ValReal) : ValReal;compilerproc;
  82. begin
  83. { Function is handled internal in the compiler }
  84. runerror(207);
  85. result:=0;
  86. end;
  87. {$define FPC_SYSTEM_HAS_SQRT}
  88. function fpc_sqrt_real(d : ValReal) : ValReal;compilerproc;
  89. begin
  90. { Function is handled internal in the compiler }
  91. runerror(207);
  92. result:=0;
  93. end;
  94. {$define FPC_SYSTEM_HAS_LN}
  95. function fpc_ln_real(d : ValReal) : ValReal;compilerproc;
  96. begin
  97. { Function is handled internal in the compiler }
  98. runerror(207);
  99. result:=0;
  100. end;
  101. const
  102. { the exact binary representation of pi (as generated by the fldpi instruction),
  103. and then divided by 2 and 4. I've tested the following FPUs and they produce
  104. the exact same values:
  105. i8087
  106. Pentium III (Coppermine)
  107. Athlon 64 (K8)
  108. }
  109. Extended_PIO2: array [0..4] of word=($C235,$2168,$DAA2,$C90F,$3FFF); { pi/2 }
  110. Extended_PIO4: array [0..4] of word=($C235,$2168,$DAA2,$C90F,$3FFE); { pi/4 }
  111. {$define FPC_SYSTEM_HAS_ARCTAN}
  112. function fpc_arctan_real(d : ValReal) : ValReal;assembler;compilerproc;
  113. var
  114. sw: word;
  115. asm
  116. { the fpatan instruction on the 8087 and 80287 has the following restrictions:
  117. 0 <= ST(1) < ST(0) < +inf
  118. which makes it useful only for calculating arctan in the range:
  119. 0 <= d < 1
  120. so in order to cover the full range, we use the following properties of arctan:
  121. arctan(1) = pi/4
  122. arctan(-d) = -arctan(d)
  123. arctan(d) = pi/2 - arctan(1/d), if d>0
  124. }
  125. fld tbyte [d]
  126. ftst
  127. fstsw sw
  128. mov ah, byte [sw + 1]
  129. sahf
  130. jb @@negative
  131. { d >= 0 }
  132. fld1 // 1 d
  133. fcom
  134. fstsw sw
  135. mov ah, byte [sw + 1]
  136. sahf
  137. jb @@greater_than_one
  138. jz @@equal_to_one
  139. { 0 <= d < 1 }
  140. fpatan
  141. jmp @@done
  142. @@greater_than_one:
  143. { d > 1 }
  144. fxch st(1) // d 1
  145. fpatan // arctan(1/d)
  146. fld tbyte [Extended_PIO2] // pi/2 arctan(1/d)
  147. fsubrp st(1), st // pi/2-arctan(1/d)
  148. jmp @@done
  149. @@equal_to_one:
  150. { d = 1, return pi/4 }
  151. fstp st
  152. fstp st
  153. fld tbyte [Extended_PIO4]
  154. jmp @@done
  155. @@negative:
  156. { d < 0; -d > 0 }
  157. fchs // -d
  158. fld1 // 1 -d
  159. fcom
  160. fstsw sw
  161. mov ah, byte [sw + 1]
  162. sahf
  163. jb @@less_than_minus_one
  164. jz @@equal_to_minus_one
  165. { -1 < d < 0; 0 < -d < 1 }
  166. fpatan // arctan(-d)
  167. fchs // -arctan(-d)
  168. jmp @@done
  169. @@equal_to_minus_one:
  170. { d = -1, return -pi/4 }
  171. fstp st
  172. fstp st
  173. fld tbyte [Extended_PIO4]
  174. fchs
  175. jmp @@done
  176. @@less_than_minus_one:
  177. { d < -1; -d > 1 }
  178. fxch st(1) // -d 1
  179. fpatan // arctan(-1/d)
  180. fld tbyte [Extended_PIO2] // pi/2 arctan(-1/d)
  181. fsubp st(1), st // arctan(-1/d)-pi/2
  182. @@done:
  183. end;
  184. {$define FPC_SYSTEM_HAS_EXP}
  185. function fpc_exp_real(d : ValReal) : ValReal;assembler;compilerproc;
  186. var
  187. sw1: word;
  188. asm
  189. // comes from DJ GPP
  190. { fixed for 8087 and 80287 by nickysn
  191. notable differences between 8087/80287 and 80387:
  192. f2xm1 on 8087/80287 requires that 0<=st(0)<=0.5
  193. f2xm1 on 80387+ requires that -1<=st(0)<=1
  194. fscale on 8087/80287 requires that -2**15<=st(1)<=0 or 1<=st(1)<2**15
  195. fscale on 80387+ has no restrictions
  196. }
  197. fld tbyte[d] // d
  198. fldl2e // l2e d
  199. fmulp st(1), st // l2e*d
  200. fld st(0) // l2e*d l2e*d
  201. frndint // round(l2e*d) l2e*d
  202. fxch st(1) // l2e*d round(l2e*d)
  203. fsub st, st(1) // l2e*d-round(l2e*d) round(l2e*d)
  204. ftst // l2e*d-round(l2e*d)<0?
  205. fstsw sw1
  206. mov ah, byte [sw1 + 1]
  207. sahf
  208. jb @@negative
  209. f2xm1 // 2**(l2e*d-round(l2e*d))-1 round(l2e*d)
  210. fld1 // 1 2**(l2e*d-round(l2e*d))-1 round(l2e*d)
  211. faddp st(1), st // 2**(l2e*d-round(l2e*d)) round(l2e*d)
  212. jmp @@common
  213. @@negative:
  214. fchs // -l2e*d+round(l2e*d) round(l2e*d)
  215. f2xm1 // 2**(-l2e*d+round(l2e*d))-1 round(l2e*d)
  216. fld1 // 1 2**(-l2e*d+round(l2e*d))-1 round(l2e*d)
  217. fadd st(1), st // 1 2**(-l2e*d+round(l2e*d)) round(l2e*d)
  218. fdivrp st(1), st // 2**(l2e*d-round(l2e*d)) round(l2e*d)
  219. @@common:
  220. fscale // (2**(l2e*d-round(l2e*d)))*(2**round(l2e*d)) round(l2e*d)
  221. fstp st(1) // (2**(l2e*d-round(l2e*d)))*(2**round(l2e*d))
  222. end;
  223. {$define FPC_SYSTEM_HAS_FRAC}
  224. function fpc_frac_real(d : ValReal) : ValReal;assembler;compilerproc;
  225. asm
  226. sub sp, 2
  227. mov bx, sp
  228. fnstcw ss:[bx]
  229. fwait
  230. mov cl, ss:[bx+1]
  231. or byte ss:[bx+1], $0f
  232. fldcw ss:[bx]
  233. fld tbyte [d]
  234. frndint
  235. fld tbyte [d]
  236. fsub st, st(1)
  237. fstp st(1)
  238. mov ss:[bx+1], cl
  239. fldcw ss:[bx]
  240. add sp, 2
  241. end;
  242. {$define FPC_SYSTEM_HAS_INT}
  243. function fpc_int_real(d : ValReal) : ValReal;assembler;compilerproc;
  244. asm
  245. sub sp, 2
  246. mov bx, sp
  247. fnstcw ss:[bx]
  248. fwait
  249. mov cl, byte ss:[bx+1]
  250. or byte ss:[bx+1], $0f
  251. fldcw ss:[bx]
  252. fwait
  253. fld tbyte [d]
  254. frndint
  255. fwait
  256. mov byte ss:[bx+1], cl
  257. fldcw ss:[bx]
  258. add sp, 2
  259. end;
  260. {$define FPC_SYSTEM_HAS_TRUNC}
  261. function fpc_trunc_real(d : ValReal) : int64;assembler;compilerproc;
  262. asm
  263. sub sp, 10
  264. mov bx, sp
  265. fld tbyte [d]
  266. fnstcw ss:[bx]
  267. mov cl, ss:[bx+1]
  268. or byte ss:[bx+1], $0f
  269. fldcw ss:[bx]
  270. mov ss:[bx+1], cl
  271. fistp qword ss:[bx+2]
  272. fldcw ss:[bx]
  273. fwait
  274. mov dx, ss:[bx+2]
  275. mov cx, ss:[bx+4]
  276. mov ax, ss:[bx+8]
  277. { store bx as last }
  278. mov bx, ss:[bx+6]
  279. add sp, 10
  280. end;
  281. {$define FPC_SYSTEM_HAS_ROUND}
  282. function fpc_round_real(d : ValReal) : int64;assembler;compilerproc;
  283. var
  284. tmp: int64;
  285. asm
  286. fld tbyte [d]
  287. fistp qword [tmp]
  288. fwait
  289. mov dx, [tmp]
  290. mov cx, [tmp+2]
  291. mov bx, [tmp+4]
  292. mov ax, [tmp+6]
  293. end;