f80sincos.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. { The code is translated from Bochs by Florian Klaempfl and contains
  2. the copyright notice below. The original filename is fsincos.cc
  3. All changes to the code are copyrighted by the Free Pascal development team and
  4. released under the same license as the orginal code, see below.
  5. }
  6. {============================================================================
  7. This source file is an extension to the SoftFloat IEC/IEEE Floating-point
  8. Arithmetic Package, Release 2b, written for Bochs (x86 achitecture simulator)
  9. floating point emulation.
  10. THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
  11. been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
  12. RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
  13. AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
  14. COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
  15. EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
  16. INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
  17. OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
  18. Derivative works are acceptable, even for commercial purposes, so long as
  19. (1) the source code for the derivative work includes prominent notice that
  20. the work is derivative, and (2) the source code includes prominent notice with
  21. these four paragraphs for those parts of this code that are retained.
  22. =============================================================================}
  23. {============================================================================
  24. * Written for Bochs (x86 achitecture simulator) by
  25. * Stanislav Shwartsman [sshwarts at sourceforge net]
  26. * ==========================================================================}
  27. { the pascal translation is based on https://github.com/lubomyr/bochs/blob/8e0b9abcd81cd24d4d9c68f7fdef2f53bc180d33/cpu/fpu/fsincos.cc
  28. if you update it, please make a note here
  29. }
  30. {$define FLOAT128}
  31. {$define USE_estimateDiv128To64}
  32. {$I f80constant.inc}
  33. const
  34. floatx80_one : floatx80 =
  35. (
  36. low: QWord($8000000000000000);
  37. high: $3fff;
  38. );
  39. { reduce trigonometric function argument using 128-bit precision
  40. M_PI approximation }
  41. function argument_reduction_kernel(aSig0: Bit64u; Exp: Integer; var zSig0, zSig1: Bit64u) : Bit64u;
  42. var
  43. aSig1, term0, term1, term2, q : Bit64u;
  44. begin
  45. aSig1 := 0;
  46. shortShift128Left(aSig1, aSig0, Exp, aSig1, aSig0);
  47. q := estimateDiv128To64(aSig1, aSig0, FLOAT_PI_HI);
  48. mul128By64To192(FLOAT_PI_HI, FLOAT_PI_LO, q, term0, term1, term2);
  49. sub128(aSig1, aSig0, term0, term1, zSig1, zSig0);
  50. while (Bit64s(zSig1) < 0) do
  51. begin
  52. dec(q);
  53. add192(zSig1, zSig0, term2, 0, FLOAT_PI_HI, FLOAT_PI_LO, zSig1, zSig0, term2);
  54. end;
  55. zSig1 := term2;
  56. Result := q;
  57. end;
  58. function reduce_trig_arg(expDiff: Integer; var zSign: Integer; var aSig0, aSig1: Bit64u) : Integer;
  59. var
  60. term0, term1, q : Bit64u;
  61. eq, lt : Integer;
  62. begin
  63. q := 0;
  64. if (expDiff < 0) then
  65. begin
  66. shift128Right(aSig0, 0, 1, aSig0, aSig1);
  67. expDiff := 0;
  68. end;
  69. if (expDiff > 0) then
  70. begin
  71. q := argument_reduction_kernel(aSig0, expDiff, aSig0, aSig1);
  72. end
  73. else
  74. begin
  75. if (FLOAT_PI_HI <= aSig0) then
  76. begin
  77. dec(aSig0, FLOAT_PI_HI);
  78. q := 1;
  79. end;
  80. end;
  81. shift128Right(FLOAT_PI_HI, FLOAT_PI_LO, 1, &term0, &term1);
  82. if (not(lt128(aSig0, aSig1, term0, term1))<>0) then
  83. begin
  84. lt := lt128(term0, term1, aSig0, aSig1);
  85. eq := eq128(aSig0, aSig1, term0, term1);
  86. if (((eq<>0) and ((q and 1)<>0)) or (lt<>0)) then
  87. begin
  88. zSign := not(zSign);
  89. inc(q);
  90. end;
  91. if (lt<>0) then
  92. sub128(FLOAT_PI_HI, FLOAT_PI_LO, aSig0, aSig1, &aSig0, &aSig1);
  93. end;
  94. Result:= Integer(q and 3);
  95. end;
  96. const
  97. sin_arr: array[0..10] of float128 = (
  98. (low: $3fff000000000000; high: $0000000000000000), { 1 }
  99. (low: $bffc555555555555; high: $5555555555555555), { 3 }
  100. (low: $3ff8111111111111; high: $1111111111111111), { 5 }
  101. (low: $bff2a01a01a01a01; high: $a01a01a01a01a01a), { 7 }
  102. (low: $3fec71de3a556c73; high: $38faac1c88e50017), { 9 }
  103. (low: $bfe5ae64567f544e; high: $38fe747e4b837dc7), { 11 }
  104. (low: $3fde6124613a86d0; high: $97ca38331d23af68), { 13 }
  105. (low: $bfd6ae7f3e733b81; high: $f11d8656b0ee8cb0), { 15 }
  106. (low: $3fce952c77030ad4; high: $a6b2605197771b00), { 17 }
  107. (low: $bfc62f49b4681415; high: $724ca1ec3b7b9675), { 19 }
  108. (low: $3fbd71b8ef6dcf57; high: $18bef146fcee6e45) { 21 }
  109. );
  110. cos_arr: array[0..10] of float128 = (
  111. (low: $3fff000000000000; high: $0000000000000000), { 0 }
  112. (low: $bffe000000000000; high: $0000000000000000), { 2 }
  113. (low: $3ffa555555555555; high: $5555555555555555), { 4 }
  114. (low: $bff56c16c16c16c1; high: $6c16c16c16c16c17), { 6 }
  115. (low: $3fefa01a01a01a01; high: $a01a01a01a01a01a), { 8 }
  116. (low: $bfe927e4fb7789f5; high: $c72ef016d3ea6679), { 10 }
  117. (low: $3fe21eed8eff8d89; high: $7b544da987acfe85), { 12 }
  118. (low: $bfda93974a8c07c9; high: $d20badf145dfa3e5), { 14 }
  119. (low: $3fd2ae7f3e733b81; high: $f11d8656b0ee8cb0), { 16 }
  120. (low: $bfca6827863b97d9; high: $77bb004886a2c2ab), { 18 }
  121. (low: $3fc1e542ba402022; high: $507a9cad2bf8f0bb) { 20 }
  122. );
  123. {$I f80poly.inc}
  124. { 0 <= x <= pi/4 }
  125. function poly_sin(x: float128): float128;inline;
  126. begin
  127. // 3 5 7 9 11 13 15
  128. // x x x x x x x
  129. // sin (x) ~ x - --- + --- - --- + --- - ---- + ---- - ---- =
  130. // 3! 5! 7! 9! 11! 13! 15!
  131. //
  132. // 2 4 6 8 10 12 14
  133. // x x x x x x x
  134. // := x * [ 1 - --- + --- - --- + --- - ---- + ---- - ---- ] =
  135. // 3! 5! 7! 9! 11! 13! 15!
  136. //
  137. // 3 3
  138. // -- 4k -- 4k+2
  139. // p(x) := > C * x > 0 q(x) := > C * x < 0
  140. // -- 2k -- 2k+1
  141. // k=0 k=0
  142. //
  143. // 2
  144. // sin(x) ~ x * [ p(x) + x * q(x) ]
  145. //
  146. Result := OddPoly(x, sin_arr, length(sin_arr));
  147. end;
  148. { 0 <= x <= pi/4 }
  149. function poly_cos(x: float128): float128;inline;
  150. begin
  151. // 2 4 6 8 10 12 14
  152. // x x x x x x x
  153. // cos (x) ~ 1 - --- + --- - --- + --- - ---- + ---- - ----
  154. // 2! 4! 6! 8! 10! 12! 14!
  155. //
  156. // 3 3
  157. // -- 4k -- 4k+2
  158. // p(x) := > C * x > 0 q(x) := > C * x < 0
  159. // -- 2k -- 2k+1
  160. // k=0 k=0
  161. //
  162. // 2
  163. // cos(x) ~ [ p(x) + x * q(x) ]
  164. //
  165. Result := EvenPoly(x, cos_arr, length(cos_arr));
  166. end;
  167. procedure sincos_invalid(sin_a: pfloatx80; cos_a: pfloatx80; a: floatx80);
  168. begin
  169. if assigned(sin_a) then
  170. sin_a^ := a;
  171. if assigned(cos_a) then
  172. cos_a^ := a;
  173. end;
  174. procedure sincos_tiny_argument(sin_a, cos_a: pfloatx80; a: floatx80);
  175. begin
  176. if assigned(sin_a) then
  177. sin_a^ := a;
  178. if assigned(cos_a) then
  179. cos_a^ := floatx80_one;
  180. end;
  181. function floatx80_chs(const a: floatx80): floatx80;
  182. begin
  183. Result := a;
  184. Result.high := Result.high xor $8000;
  185. end;
  186. function sincos_approximation(neg: Integer; r: float128; quotient: Bit64u): floatx80;
  187. begin
  188. if (quotient and $1)<>0 then
  189. begin
  190. r := poly_cos(r);
  191. neg := 0;
  192. end
  193. else
  194. begin
  195. r := poly_sin(r);
  196. end;
  197. result := float128_to_floatx80(r);
  198. if (quotient and $2)<>0 then
  199. neg := neg xor 1;
  200. if (neg<>0) then
  201. floatx80_chs(result);
  202. end;
  203. // =================================================
  204. // FSINCOS Compute sin(x) and cos(x)
  205. // =================================================
  206. //
  207. // Uses the following identities:
  208. // ----------------------------------------------------------
  209. //
  210. // sin(-x) := -sin(x)
  211. // cos(-x) := cos(x)
  212. //
  213. // sin(x+y) := sin(x)*cos(y)+cos(x)*sin(y)
  214. // cos(x+y) := sin(x)*sin(y)+cos(x)*cos(y)
  215. //
  216. // sin(x+ pi/2) := cos(x)
  217. // sin(x+ pi) := -sin(x)
  218. // sin(x+3pi/2) := -cos(x)
  219. // sin(x+2pi) := sin(x)
  220. //
  221. function fsincos(a: floatx80;sin_a, cos_a: pfloatx80): Integer;
  222. var
  223. aSig0, aSig1: Bit64u;
  224. aExp, zExp, expDiff: Bit32s;
  225. aSign, zSign, q: Integer;
  226. r: float128;
  227. label
  228. invalid;
  229. begin
  230. aSig1 := 0;
  231. q := 0;
  232. // handle unsupported extended double-precision floating encodings
  233. if (floatx80_is_unsupported(a)<>0) then
  234. begin
  235. goto invalid;
  236. end;
  237. aSig0 := extractFloatx80Frac(a);
  238. aExp := extractFloatx80Exp(a);
  239. aSign := extractFloatx80Sign(a);
  240. { invalid argument }
  241. if (aExp = $7FFF) then
  242. begin
  243. if (Bit64u(aSig0 shl 1)<>0) then
  244. begin
  245. sincos_invalid(sin_a, cos_a, propagateFloatx80NaN(a, a)); // not sure if this is correctly translated (FK)
  246. Result := 0;
  247. Exit;
  248. end;
  249. invalid:
  250. float_raise(float_flag_invalid);
  251. sincos_invalid(sin_a, cos_a, floatx80_default_nan);
  252. Result := 0;
  253. Exit;
  254. end;
  255. if (aExp = 0) then
  256. begin
  257. if (aSig0 = 0) then
  258. begin
  259. sincos_tiny_argument(sin_a, cos_a, a);
  260. Result := 0;
  261. Exit;
  262. end;
  263. float_raise(float_flag_denormal);
  264. { handle pseudo denormals }
  265. if (not(aSig0 and QWord($8000000000000000))<>0) then
  266. begin
  267. float_raise(float_flag_inexact);
  268. if assigned(sin_a) then
  269. float_raise(float_flag_underflow);
  270. sincos_tiny_argument(sin_a, cos_a, a);
  271. Result := 0;
  272. Exit;
  273. end;
  274. normalizeFloatx80Subnormal(aSig0, aExp, aSig0);
  275. end;
  276. zSign := aSign;
  277. zExp := FLOATX80_EXP_BIAS;
  278. expDiff := aExp - zExp;
  279. { argument is out-of-range }
  280. if (expDiff >= 63) then
  281. Exit(-1);
  282. float_raise(float_flag_inexact);
  283. if (expDiff < -1) then
  284. begin // doesn't require reduction
  285. if (expDiff <= -68) then
  286. begin
  287. a := packFloatx80(aSign, aExp, aSig0);
  288. sincos_tiny_argument(sin_a, cos_a, a);
  289. Result := 0;
  290. Exit;
  291. end;
  292. zExp := aExp;
  293. end
  294. else begin
  295. q := reduce_trig_arg(expDiff, zSign, aSig0, aSig1);
  296. end;
  297. { **************************** }
  298. { argument reduction completed }
  299. { **************************** }
  300. { using float128 for approximation }
  301. r := normalizeRoundAndPackFloat128(0, zExp-$10, aSig0, aSig1);
  302. if (aSign<>0) then
  303. q := -q;
  304. if assigned(sin_a) then
  305. sin_a^ := sincos_approximation(zSign, r, q);
  306. if assigned(cos_a) then
  307. cos_a^ := sincos_approximation(zSign, r, q+1);
  308. Result := 0;
  309. end;
  310. function fsin(var a: floatx80): Integer;
  311. begin
  312. Result := fsincos(a, @a, nil);
  313. end;
  314. function fcos(var a: floatx80): Integer;
  315. begin
  316. Result := fsincos(a, nil, @a);
  317. end;
  318. // =================================================
  319. // FPTAN Compute tan(x)
  320. // =================================================
  321. //
  322. // Uses the following identities:
  323. //
  324. // 1. ----------------------------------------------------------
  325. //
  326. // sin(-x) := -sin(x)
  327. // cos(-x) := cos(x)
  328. //
  329. // sin(x+y) := sin(x)*cos(y)+cos(x)*sin(y)
  330. // cos(x+y) := sin(x)*sin(y)+cos(x)*cos(y)
  331. //
  332. // sin(x+ pi/2) := cos(x)
  333. // sin(x+ pi) := -sin(x)
  334. // sin(x+3pi/2) := -cos(x)
  335. // sin(x+2pi) := sin(x)
  336. //
  337. // 2. ----------------------------------------------------------
  338. //
  339. // sin(x)
  340. // tan(x) := ------
  341. // cos(x)
  342. //
  343. function ftan(var a: floatx80): Integer;
  344. var
  345. aSig0, aSig1: Bit64u;
  346. aExp, zExp, expDiff: Bit32s;
  347. aSign, zSign, q: Integer;
  348. r, cos_r, sin_r: float128;
  349. label
  350. invalid;
  351. begin
  352. aSig1 := 0;
  353. q := 0;
  354. // handle unsupported extended double-precision floating encodings
  355. if (floatx80_is_unsupported(a)<>0) then
  356. begin
  357. goto invalid;
  358. end;
  359. aSig0 := extractFloatx80Frac(a);
  360. aExp := extractFloatx80Exp(a);
  361. aSign := extractFloatx80Sign(a);
  362. { invalid argument }
  363. if (aExp = $7FFF) then
  364. begin
  365. if (Bit64u(aSig0 shl 1)<>0) then
  366. begin
  367. a := propagateFloatx80NaN(a, a); // not sure if this is correctly translated (FK)
  368. Result := 0;
  369. Exit;
  370. end;
  371. invalid:
  372. float_raise(float_flag_invalid);
  373. a := floatx80_default_nan;
  374. Result := 0;
  375. Exit;
  376. end;
  377. if (aExp = 0) then
  378. begin
  379. if (aSig0 = 0) then
  380. Exit(0);
  381. float_raise(float_flag_denormal);
  382. { handle pseudo denormals }
  383. if (not(aSig0 and QWord($8000000000000000))<>0) then
  384. begin
  385. float_raise([float_flag_inexact,float_flag_underflow]);
  386. Result := 0;
  387. Exit;
  388. end;
  389. normalizeFloatx80Subnormal(aSig0, aExp, aSig0);
  390. end;
  391. zSign := aSign;
  392. zExp := FLOATX80_EXP_BIAS;
  393. expDiff := aExp - zExp;
  394. { argument is out-of-range }
  395. if (expDiff >= 63) then
  396. Exit(-1);
  397. float_raise(float_flag_inexact);
  398. if (expDiff < -1) then
  399. begin // doesn't require reduction
  400. if (expDiff <= -68) then
  401. begin
  402. a := packFloatx80(aSign, aExp, aSig0);
  403. Result := 0;
  404. exit;
  405. end;
  406. zExp := aExp;
  407. end
  408. else
  409. begin
  410. q := reduce_trig_arg(expDiff, zSign, aSig0, aSig1);
  411. end;
  412. { **************************** }
  413. { argument reduction completed }
  414. { **************************** }
  415. { using float128 for approximation }
  416. r := normalizeRoundAndPackFloat128(0, zExp-$10, aSig0, aSig1);
  417. sin_r := poly_sin(r);
  418. cos_r := poly_cos(r);
  419. if (q and $1)<>0 then
  420. begin
  421. r := float128_div(cos_r, sin_r);
  422. zSign := zSign xor 1;
  423. end
  424. else
  425. begin
  426. r := float128_div(sin_r, cos_r);
  427. end;
  428. a := float128_to_floatx80(r);
  429. if (zSign<>0) then
  430. floatx80_chs(a);
  431. Result := 0;
  432. end;