math.inc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 2000 by Jonas Maebe and other members of the
  5. Free Pascal development team
  6. Implementation of mathematical Routines (only for real)
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. const
  14. longint_to_real_helper: int64 = $4330000080000000;
  15. cardinal_to_real_helper: int64 = $4330000000000000;
  16. int_to_real_factor: double = double(high(cardinal))+1.0;
  17. {****************************************************************************
  18. EXTENDED data type routines
  19. ****************************************************************************}
  20. {$define FPC_SYSTEM_HAS_PI}
  21. function pi : double;[internproc:in_pi];
  22. {$define FPC_SYSTEM_HAS_ABS}
  23. function abs(d : extended) : extended;[internproc:in_abs_extended];
  24. {$define FPC_SYSTEM_HAS_SQR}
  25. function sqr(d : extended) : extended;[internproc:in_sqr_extended];
  26. {
  27. function arctan(d : extended) : extended;[internconst:in_arctan_extended];
  28. begin
  29. runerror(207);
  30. end;
  31. function ln(d : extended) : extended;[internconst:in_ln_extended];
  32. begin
  33. runerror(207);
  34. end;
  35. function sin(d : extended) : extended;[internconst: in_sin_extended];
  36. begin
  37. runerror(207);
  38. end;
  39. function cos(d : extended) : extended;[internconst:in_cos_extended];
  40. begin
  41. runerror(207);
  42. end;
  43. function exp(d : extended) : extended;[internconst:in_const_exp];
  44. begin
  45. runerror(207);
  46. end;
  47. }
  48. const
  49. factor: double = double(int64(1) shl 32);
  50. factor2: double = double(int64(1) shl 31);
  51. {$define FPC_SYSTEM_HAS_TRUNC}
  52. function trunc(d : extended) : int64;assembler;[internconst:in_const_trunc];
  53. { input: d in fr1 }
  54. { output: result in r3 }
  55. assembler;
  56. var
  57. temp: packed record
  58. case byte of
  59. 0: (l1,l2: longint);
  60. 1: (d: double);
  61. end;
  62. asm
  63. // store d in temp
  64. stfd f1,temp
  65. // extract sign bit (record in cr0)
  66. lwz r3,temp
  67. rlwinm. r3,r3,1,31,31
  68. // make d positive
  69. fabs f1,f1
  70. // load 2^32 in f2
  71. {$ifndef macos}
  72. lis r4,factor@ha
  73. lfd f2,factor@l(r4)
  74. {$else}
  75. lwz r4,factor(r2)
  76. lfd f2,0(r4)
  77. {$endif}
  78. // check if value is < 0
  79. // f3 := d / 2^32;
  80. fdiv f3,f1,f2
  81. // round
  82. fctiwz f4,f3
  83. // store
  84. stfd f4,temp
  85. // and load into r4
  86. lwz r3,temp+4
  87. // convert back to float
  88. lis r0,0x4330
  89. stw r0,temp
  90. xoris r0,r3,0x8000
  91. stw r0,temp+4
  92. {$ifndef macos}
  93. lis r4,longint_to_real_helper@ha
  94. lfd f0,longint_to_real_helper@l(r4)
  95. {$else}
  96. lwz r4,longint_to_real_helper(r2)
  97. lfd f0,0(r4)
  98. {$endif}
  99. lfd f3,temp
  100. fsub f3,f3,f0
  101. // f4 := d "mod" 2^32 ( = d - ((d / 2^32) * 2^32))
  102. fnmsub f4,f3,f2,f1
  103. // now, convert to unsigned 32 bit
  104. // load 2^31 in f2
  105. {$ifndef macos}
  106. lis r4,factor2@ha
  107. lfd f2,factor2@l(r4)
  108. {$else}
  109. lwz r4,factor2(r2)
  110. lfd f2,0(r4)
  111. {$endif}
  112. // subtract 2^31
  113. fsub f3,f4,f2
  114. // was the value > 2^31?
  115. fcmpu cr1,f4,f2
  116. // use diff if >= 2^31
  117. fsel f4,f3,f3,f4
  118. // next part same as conversion to signed integer word
  119. fctiwz f4,f4
  120. stfd f4,temp
  121. lwz r4,temp+4
  122. // add 2^31 if value was >=2^31
  123. blt cr1, .LTruncNoAdd
  124. xoris r4,r4,0x8000
  125. .LTruncNoAdd:
  126. // negate value if it was negative to start with
  127. beq cr0,.LTruncPositive
  128. subfic r4,r4,0
  129. subfze r3,r3
  130. .LTruncPositive:
  131. end;
  132. {$define FPC_SYSTEM_HAS_ROUND}
  133. {$ifdef hascompilerproc}
  134. function round(d : extended) : int64;[internconst:in_const_round, external name 'FPC_ROUND'];
  135. function fpc_round(d : extended) : int64;assembler;[public, alias:'FPC_ROUND'];{$ifdef hascompilerproc}compilerproc;{$endif hascompilerproc}
  136. {$else}
  137. function round(d : extended) : int64;assembler;[internconst:in_const_round];
  138. {$endif hascompilerproc}
  139. { exactly the same as trunc, except that one fctiwz has become fctiw }
  140. { input: d in fr1 }
  141. { output: result in r3 }
  142. assembler;
  143. var
  144. temp: packed record
  145. case byte of
  146. 0: (l1,l2: longint);
  147. 1: (d: double);
  148. end;
  149. asm
  150. // store d in temp
  151. stfd f1, temp
  152. // extract sign bit (record in cr0)
  153. lwz r4,temp
  154. rlwinm. r4,r4,1,31,31
  155. // make d positive
  156. fabs f1,f1
  157. // load 2^32 in f2
  158. {$ifndef macos}
  159. lis r4,factor@ha
  160. lfd f2,factor@l(r4)
  161. {$else}
  162. lwz r4,factor(r2)
  163. lfd f2,0(r4)
  164. {$endif}
  165. // check if value is < 0
  166. // f3 := d / 2^32;
  167. fdiv f3,f1,f2
  168. // round
  169. fctiwz f4,f3
  170. // store
  171. stfd f4,temp
  172. // and load into r4
  173. lwz r3,temp+4
  174. // convert back to float
  175. lis r0,0x4330
  176. stw r0,temp
  177. xoris r0,r3,0x8000
  178. stw r0,temp+4
  179. {$ifndef macos}
  180. lis r4,longint_to_real_helper@ha
  181. lfd f0,longint_to_real_helper@l(r4)
  182. {$else}
  183. lwz r4,longint_to_real_helper(r2)
  184. lfd f0,0(r4)
  185. {$endif}
  186. lfd f3,temp
  187. fsub f3,f3,f0
  188. // f4 := d "mod" 2^32 ( = d - ((d / 2^32) * 2^32))
  189. fnmsub f4,f3,f2,f1
  190. // now, convert to unsigned 32 bit
  191. // load 2^31 in f2
  192. {$ifndef macos}
  193. lis r4,factor2@ha
  194. lfd f2,factor2@l(r4)
  195. {$else}
  196. lwz r4,factor2(r2)
  197. lfd f2,0(r4)
  198. {$endif}
  199. // subtract 2^31
  200. fsub f3,f4,f2
  201. // was the value > 2^31?
  202. fcmpu cr1,f4,f2
  203. // use diff if >= 2^31
  204. fsel f4,f3,f3,f4
  205. // next part same as conversion to signed integer word
  206. fctiw f4,f4
  207. stfd f4,temp
  208. lwz r4,temp+4
  209. // add 2^31 if value was >=2^31
  210. blt cr1, .LRoundNoAdd
  211. xoris r4,r4,0x8000
  212. .LRoundNoAdd:
  213. // negate value if it was negative to start with
  214. beq cr0,.LRoundPositive
  215. subfic r4,r4,0
  216. subfze r3,r3
  217. .LRoundPositive:
  218. end;
  219. {$define FPC_SYSTEM_HAS_POWER}
  220. function power(bas,expo : extended) : extended;
  221. begin
  222. if bas=0 then
  223. begin
  224. if expo<>0 then
  225. power:=0.0
  226. else
  227. HandleError(207);
  228. end
  229. else if expo=0 then
  230. power:=1
  231. else
  232. { bas < 0 is not allowed }
  233. if bas<0 then
  234. handleerror(207)
  235. else
  236. power:=exp(ln(bas)*expo);
  237. end;
  238. {****************************************************************************
  239. Longint data type routines
  240. ****************************************************************************}
  241. {$define FPC_SYSTEM_HAS_POWER_INT64}
  242. function power(bas,expo : int64) : int64;
  243. begin
  244. if bas=0 then
  245. begin
  246. if expo<>0 then
  247. power:=0
  248. else
  249. HandleError(207);
  250. end
  251. else if expo=0 then
  252. power:=1
  253. else
  254. begin
  255. if bas<0 then
  256. begin
  257. if odd(expo) then
  258. power:=-round(exp(ln(-bas)*expo))
  259. else
  260. power:=round(exp(ln(-bas)*expo));
  261. end
  262. else
  263. power:=round(exp(ln(bas)*expo));
  264. end;
  265. end;
  266. {****************************************************************************
  267. Helper routines to support old TP styled reals
  268. ****************************************************************************}
  269. { warning: the following converts a little-endian TP-style real }
  270. { to a big-endian double. So don't byte-swap the TP real! }
  271. {$define FPC_SYSTEM_HAS_REAL2DOUBLE}
  272. function real2double(r : real48) : double;
  273. var
  274. res : array[0..7] of byte;
  275. exponent : word;
  276. begin
  277. { copy mantissa }
  278. res[6]:=0;
  279. res[5]:=r[1] shl 5;
  280. res[4]:=(r[1] shr 3) or (r[2] shl 5);
  281. res[3]:=(r[2] shr 3) or (r[3] shl 5);
  282. res[2]:=(r[3] shr 3) or (r[4] shl 5);
  283. res[1]:=(r[4] shr 3) or (r[5] and $7f) shl 5;
  284. res[0]:=(r[5] and $7f) shr 3;
  285. { copy exponent }
  286. { correct exponent: }
  287. exponent:=(word(r[0])+(1023-129));
  288. res[1]:=res[1] or ((exponent and $f) shl 4);
  289. res[0]:=exponent shr 4;
  290. { set sign }
  291. res[0]:=res[0] or (r[5] and $80);
  292. real2double:=double(res);
  293. end;
  294. {****************************************************************************
  295. Int to real helpers
  296. ****************************************************************************}
  297. {$define FPC_SYSTEM_HAS_INT64_TO_DOUBLE}
  298. function fpc_int64_to_double(i: int64): double; compilerproc;
  299. assembler;
  300. { input: high(i) in r4, low(i) in r3 }
  301. { output: double(i) in f0 }
  302. var
  303. temp: packed record
  304. case byte of
  305. 0: (l1,l2: cardinal);
  306. 1: (d: double);
  307. end;
  308. asm
  309. lis r0,0x4330
  310. stw r0,temp
  311. xoris r3,r3,0x8000
  312. stw r3,temp+4
  313. {$ifndef macos}
  314. lis r3,longint_to_real_helper@ha
  315. lfd f1,longint_to_real_helper@l(r3)
  316. {$else}
  317. lwz r3,longint_to_real_helper(r2)
  318. lfd f1,0(r3)
  319. {$endif}
  320. lfd f0,temp
  321. stw r4,temp+4
  322. fsub f0,f0,f1
  323. {$ifndef macos}
  324. lis r4,cardinal_to_real_helper@ha
  325. lfd f1,cardinal_to_real_helper@l(r4)
  326. lis r4,int_to_real_factor@ha
  327. lfd f3,temp
  328. lfd f2,int_to_real_factor@l(r4)
  329. {$else}
  330. lwz r4,cardinal_to_real_helper(r2)
  331. lwz r3,int_to_real_factor(r2)
  332. lfd f3,temp
  333. lfd f1,0(r4)
  334. lfd f2,0(r3)
  335. {$endif}
  336. fsub f3,f3,f1
  337. fmadd f1,f0,f2,f3
  338. end;
  339. {$define FPC_SYSTEM_HAS_QWORD_TO_DOUBLE}
  340. function fpc_qword_to_double(q: qword): double; compilerproc;
  341. assembler;
  342. { input: high(q) in r4, low(q) in r3 }
  343. { output: double(q) in f0 }
  344. var
  345. temp: packed record
  346. case byte of
  347. 0: (l1,l2: cardinal);
  348. 1: (d: double);
  349. end;
  350. asm
  351. lis r0,0x4330
  352. stw r0,temp
  353. stw r3,temp+4
  354. lfd f0,temp
  355. {$ifndef macos}
  356. lis r3,cardinal_to_real_helper@ha
  357. lfd f1,cardinal_to_real_helper@l(r3)
  358. {$else}
  359. lwz r3,longint_to_real_helper(r2)
  360. lfd f1,0(r3)
  361. {$endif}
  362. stw r4,temp+4
  363. fsub f0,f0,f1
  364. lfd f3,temp
  365. {$ifndef macos}
  366. lis r4,int_to_real_factor@ha
  367. lfd f2,int_to_real_factor@l(r4)
  368. {$else}
  369. lwz r4,int_to_real_factor(r2)
  370. lfd f2,0(r4)
  371. {$endif}
  372. fsub f3,f3,f1
  373. fmadd f1,f0,f2,f3
  374. end;
  375. {
  376. $Log$
  377. Revision 1.33 2004-02-09 20:21:06 olle
  378. * fixed global variable access in asm
  379. Revision 1.32 2003/12/07 19:55:37 jonas
  380. - reverted previous patch, solved with the new assembler reader
  381. (which didn't understand the new syntax)
  382. Revision 1.30 2003/11/15 19:01:27 florian
  383. * fixed rtl to work with the integrated fpc ppc assembler reader
  384. Revision 1.29 2003/09/04 16:07:31 florian
  385. * fixed qword_to_double conversion on powerpc
  386. Revision 1.28 2003/09/03 14:09:37 florian
  387. * arm fixes to the common rtl code
  388. * some generic math code fixed
  389. * ...
  390. Revision 1.27 2003/08/08 22:02:05 olle
  391. * small bugfix macos
  392. Revision 1.26 2003/06/14 12:41:08 jonas
  393. * fixed compilation problems (removed unnecessary modified registers
  394. lists from procedures)
  395. Revision 1.25 2003/05/31 20:22:06 jonas
  396. * fixed 64 bit results of trunc and round
  397. Revision 1.24 2003/05/30 23:56:41 florian
  398. * fixed parameter passing for int64
  399. Revision 1.23 2003/05/24 13:39:32 jonas
  400. * fsqrt is an optional instruction in the ppc architecture and isn't
  401. implemented by any current ppc afaik, so use the generic sqrt routine
  402. instead (adapted so it works with compilerproc)
  403. Revision 1.22 2003/05/16 16:04:33 jonas
  404. * fixed round() (almost the same as trunc)
  405. Revision 1.21 2003/05/11 18:09:45 jonas
  406. * fixed qword and int64 to double conversion
  407. Revision 1.20 2003/05/02 15:12:19 jonas
  408. - removed empty ppc-specific frac()
  409. + added correct generic frac() implementation for doubles (translated
  410. from glibc code)
  411. Revision 1.19 2003/04/26 20:36:24 jonas
  412. * trunc now also supports int64 (no NaN's etc though)
  413. Revision 1.18 2003/04/26 17:20:16 florian
  414. * fixed trunc, now it's working at least for longint range
  415. Revision 1.17 2003/04/23 21:28:21 peter
  416. * fpc_round added, needed for int64 currency
  417. Revision 1.16 2003/01/16 11:29:11 olle
  418. * changed access of globals to be indirect via TOC
  419. Revision 1.15 2003/01/15 01:09:04 florian
  420. * changed power(...) prototype to int64
  421. Revision 1.14 2002/11/28 11:04:16 olle
  422. * macos: refs to globals in asm adapted to macos
  423. Revision 1.13 2002/10/21 18:08:28 jonas
  424. * round has int64 instead of longint result
  425. Revision 1.12 2002/09/08 13:00:21 jonas
  426. * made pi an internproc instead of internconst
  427. Revision 1.11 2002/09/07 16:01:26 peter
  428. * old logs removed and tabs fixed
  429. Revision 1.10 2002/08/18 22:11:10 florian
  430. * fixed remaining assembler errors
  431. Revision 1.9 2002/08/18 21:37:48 florian
  432. * several errors in inline assembler fixed
  433. Revision 1.8 2002/08/10 17:14:36 jonas
  434. * various fixes, mostly changing the names of the modifies registers to
  435. upper case since that seems to be required by the compiler
  436. Revision 1.7 2002/07/31 16:58:12 jonas
  437. * fixed conversion from int64/qword to double errors
  438. Revision 1.6 2002/07/29 21:28:17 florian
  439. * several fixes to get further with linux/ppc system unit compilation
  440. Revision 1.5 2002/07/28 21:39:29 florian
  441. * made abs a compiler proc if it is generic
  442. Revision 1.4 2002/07/28 20:43:49 florian
  443. * several fixes for linux/powerpc
  444. * several fixes to MT
  445. }