dmath_zig.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. // Math functions ported from the Zig standard library, which in turn ports
  2. // them from musl (MIT licensed):
  3. // https://github.com/ziglang/zig/blob/master/lib/std/math/
  4. //
  5. // These live in their own file so the provenance stays obvious: each function
  6. // below is a line-by-line translation of the Zig source linked above it, and
  7. // should be re-synced from there rather than hand-tuned.
  8. #include "pocketpy/common/dmath.h"
  9. #include <stdint.h>
  10. // Same layout as dmath.c's Float64Bits; kept distinct because pocketpy builds
  11. // all sources as a single unity translation unit.
  12. union ZigF64 {
  13. double f;
  14. uint64_t i;
  15. };
  16. // https://github.com/ziglang/zig/blob/master/lib/std/math/asin.zig
  17. static double zig_r64(double z) {
  18. const double pS0 = 1.66666666666666657415e-01;
  19. const double pS1 = -3.25565818622400915405e-01;
  20. const double pS2 = 2.01212532134862925881e-01;
  21. const double pS3 = -4.00555345006794114027e-02;
  22. const double pS4 = 7.91534994289814532176e-04;
  23. const double pS5 = 3.47933107596021167570e-05;
  24. const double qS1 = -2.40339491173441421878e+00;
  25. const double qS2 = 2.02094576023350569471e+00;
  26. const double qS3 = -6.88283971605453293030e-01;
  27. const double qS4 = 7.70381505559019352791e-02;
  28. double p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5)))));
  29. double q = 1.0 + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4)));
  30. return p / q;
  31. }
  32. // https://github.com/ziglang/zig/blob/master/lib/std/math/asin.zig
  33. double dmath_asin(double x) {
  34. if(!(x >= -1 && x <= 1)) return DMATH_NAN;
  35. const double pio2_hi = 1.57079632679489655800e+00;
  36. const double pio2_lo = 6.12323399573676603587e-17;
  37. union ZigF64 ux_union;
  38. ux_union.f = x;
  39. uint64_t ux = ux_union.i;
  40. uint32_t hx = (uint32_t)(ux >> 32);
  41. uint32_t ix = hx & 0x7FFFFFFF;
  42. /* |x| >= 1 or nan */
  43. if(ix >= 0x3FF00000) {
  44. uint32_t lx = (uint32_t)(ux & 0xFFFFFFFF);
  45. /* asin(1) = +-pi/2 with inexact */
  46. if(((ix - 0x3FF00000) | lx) == 0) {
  47. return x * pio2_hi + 0x1.0p-120;
  48. } else {
  49. return DMATH_NAN;
  50. }
  51. }
  52. /* |x| < 0.5 */
  53. if(ix < 0x3FE00000) {
  54. /* if 0x1p-1022 <= |x| < 0x1p-26 avoid raising overflow */
  55. if(ix < 0x3E500000 && ix >= 0x00100000) {
  56. return x;
  57. } else {
  58. return x + x * zig_r64(x * x);
  59. }
  60. }
  61. /* 1 > |x| >= 0.5 */
  62. double z = (1 - dmath_fabs(x)) * 0.5;
  63. double s = dmath_sqrt(z);
  64. double r = zig_r64(z);
  65. double fx;
  66. /* |x| > 0.975 */
  67. if(ix >= 0x3FEF3333) {
  68. fx = pio2_hi - 2 * (s + s * r);
  69. } else {
  70. union ZigF64 jx_union = {.f = s};
  71. uint64_t jx = jx_union.i;
  72. union ZigF64 df_union = {.i = jx & 0xFFFFFFFF00000000ULL};
  73. double df = df_union.f;
  74. double c = (z - df * df) / (s + df);
  75. fx = 0.5 * pio2_hi - (2 * s * r - (pio2_lo - 2 * c) - (0.5 * pio2_hi - 2 * df));
  76. }
  77. if(hx >> 31 != 0) {
  78. return -fx;
  79. } else {
  80. return fx;
  81. }
  82. }
  83. // https://github.com/ziglang/zig/blob/master/lib/std/math/acos.zig
  84. double dmath_acos(double x) {
  85. const double pio2_hi = 1.57079632679489655800e+00;
  86. const double pio2_lo = 6.12323399573676603587e-17;
  87. union ZigF64 ux_union = {.f = x};
  88. uint64_t ux = ux_union.i;
  89. uint32_t hx = (uint32_t)(ux >> 32);
  90. uint32_t ix = hx & 0x7FFFFFFF;
  91. /* |x| >= 1 or nan */
  92. if(ix >= 0x3FF00000) {
  93. uint32_t lx = (uint32_t)(ux & 0xFFFFFFFF);
  94. /* acos(1) = 0, acos(-1) = pi */
  95. if(((ix - 0x3FF00000) | lx) == 0) {
  96. if(hx >> 31 != 0) {
  97. return 2 * pio2_hi + 0x1.0p-120;
  98. } else {
  99. return 0;
  100. }
  101. }
  102. return DMATH_NAN;
  103. }
  104. /* |x| < 0.5 */
  105. if(ix < 0x3FE00000) {
  106. /* |x| < 0x1p-57 */
  107. if(ix <= 0x3C600000) {
  108. return pio2_hi + 0x1.0p-120;
  109. } else {
  110. return pio2_hi - (x - (pio2_lo - x * zig_r64(x * x)));
  111. }
  112. }
  113. /* x < -0.5 */
  114. if(hx >> 31 != 0) {
  115. double z = (1.0 + x) * 0.5;
  116. double s = dmath_sqrt(z);
  117. double w = zig_r64(z) * s - pio2_lo;
  118. return 2 * (pio2_hi - (s + w));
  119. }
  120. /* x > 0.5 */
  121. double z = (1.0 - x) * 0.5;
  122. double s = dmath_sqrt(z);
  123. union ZigF64 jx_union = {.f = s};
  124. union ZigF64 df_union = {.i = jx_union.i & 0xFFFFFFFF00000000ULL};
  125. double df = df_union.f;
  126. double c = (z - df * df) / (s + df);
  127. double w = zig_r64(z) * s + c;
  128. return 2 * (df + w);
  129. }
  130. // https://github.com/ziglang/zig/blob/master/lib/std/math/atan.zig
  131. double dmath_atan(double x) {
  132. static const double atanhi[] = {
  133. 4.63647609000806093515e-01, /* atan(0.5)hi */
  134. 7.85398163397448278999e-01, /* atan(1.0)hi */
  135. 9.82793723247329054082e-01, /* atan(1.5)hi */
  136. 1.57079632679489655800e+00, /* atan(inf)hi */
  137. };
  138. static const double atanlo[] = {
  139. 2.26987774529616870924e-17, /* atan(0.5)lo */
  140. 3.06161699786838301793e-17, /* atan(1.0)lo */
  141. 1.39033110312309984516e-17, /* atan(1.5)lo */
  142. 6.12323399573676603587e-17, /* atan(inf)lo */
  143. };
  144. static const double aT[] = {
  145. 3.33333333333329318027e-01,
  146. -1.99999999998764832476e-01,
  147. 1.42857142725034663711e-01,
  148. -1.11111104054623557880e-01,
  149. 9.09088713343650656196e-02,
  150. -7.69187620504482999495e-02,
  151. 6.66107313738753120669e-02,
  152. -5.83357013379057348645e-02,
  153. 4.97687799461593236017e-02,
  154. -3.65315727442169155270e-02,
  155. 1.62858201153657823623e-02,
  156. };
  157. union ZigF64 ux = {.f = x};
  158. uint32_t ix = (uint32_t)(ux.i >> 32);
  159. uint32_t sign = ix >> 31;
  160. int id;
  161. double z, w, s1, s2;
  162. ix &= 0x7FFFFFFF;
  163. /* |x| >= 2^66 */
  164. if(ix >= 0x44100000) {
  165. if(dmath_isnan(x)) return x;
  166. z = atanhi[3] + 0x1.0p-120;
  167. return sign != 0 ? -z : z;
  168. }
  169. /* |x| < 0.4375 */
  170. if(ix < 0x3FDC0000) {
  171. /* |x| < 0x1p-27 */
  172. if(ix < 0x3E400000) return x;
  173. id = -1;
  174. } else {
  175. x = dmath_fabs(x);
  176. /* |x| < 1.1875 */
  177. if(ix < 0x3FF30000) {
  178. /* 7/16 <= |x| < 11/16 */
  179. if(ix < 0x3FE60000) {
  180. id = 0;
  181. x = (2.0 * x - 1.0) / (2.0 + x);
  182. } else {
  183. /* 11/16 <= |x| < 19/16 */
  184. id = 1;
  185. x = (x - 1.0) / (x + 1.0);
  186. }
  187. } else {
  188. /* |x| < 2.4375 */
  189. if(ix < 0x40038000) {
  190. id = 2;
  191. x = (x - 1.5) / (1.0 + 1.5 * x);
  192. } else {
  193. /* 2.4375 <= |x| < 2^66 */
  194. id = 3;
  195. x = -1.0 / x;
  196. }
  197. }
  198. }
  199. z = x * x;
  200. w = z * z;
  201. s1 = z * (aT[0] + w * (aT[2] + w * (aT[4] + w * (aT[6] + w * (aT[8] + w * aT[10])))));
  202. s2 = w * (aT[1] + w * (aT[3] + w * (aT[5] + w * (aT[7] + w * aT[9]))));
  203. if(id < 0) return x - x * (s1 + s2);
  204. z = atanhi[id] - ((x * (s1 + s2) - atanlo[id]) - x);
  205. return sign != 0 ? -z : z;
  206. }
  207. // https://github.com/ziglang/zig/blob/master/lib/std/math/atan2.zig
  208. double dmath_atan2(double y, double x) {
  209. const double pi = 3.1415926535897931160E+00; /* 0x400921FB, 0x54442D18 */
  210. const double pi_lo = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */
  211. double z;
  212. uint32_t m, lx, ly, ix, iy;
  213. if(dmath_isnan(x) || dmath_isnan(y)) return x + y;
  214. union ZigF64 ux = {.f = x}, uy = {.f = y};
  215. ix = (uint32_t)(ux.i >> 32);
  216. lx = (uint32_t)(ux.i & 0xFFFFFFFF);
  217. iy = (uint32_t)(uy.i >> 32);
  218. ly = (uint32_t)(uy.i & 0xFFFFFFFF);
  219. /* x = 1.0 */
  220. if(((ix - 0x3FF00000) | lx) == 0) return dmath_atan(y);
  221. m = ((iy >> 31) & 1) | ((ix >> 30) & 2); /* 2 * sign(x) + sign(y) */
  222. ix &= 0x7FFFFFFF;
  223. iy &= 0x7FFFFFFF;
  224. /* when y = 0 */
  225. if((iy | ly) == 0) {
  226. switch(m) {
  227. case 0:
  228. case 1: return y; /* atan(+-0, +anything) = +-0 */
  229. case 2: return pi; /* atan(+0, -anything) = pi */
  230. default: return -pi; /* atan(-0, -anything) = -pi */
  231. }
  232. }
  233. /* when x = 0 */
  234. if((ix | lx) == 0) return m & 1 ? -pi / 2 : pi / 2;
  235. /* when x is INF */
  236. if(ix == 0x7FF00000) {
  237. if(iy == 0x7FF00000) {
  238. switch(m) {
  239. case 0: return pi / 4; /* atan(+INF, +INF) */
  240. case 1: return -pi / 4; /* atan(-INF, +INF) */
  241. case 2: return 3 * pi / 4; /* atan(+INF, -INF) */
  242. default: return -3 * pi / 4; /* atan(-INF, -INF) */
  243. }
  244. } else {
  245. switch(m) {
  246. case 0: return 0.0; /* atan(+..., +INF) */
  247. case 1: return -0.0; /* atan(-..., +INF) */
  248. case 2: return pi; /* atan(+..., -INF) */
  249. default: return -pi; /* atan(-..., -INF) */
  250. }
  251. }
  252. }
  253. /* |y/x| > 0x1p64 */
  254. if(ix + (64 << 20) < iy || iy == 0x7FF00000) return m & 1 ? -pi / 2 : pi / 2;
  255. /* z = atan(|y/x|) without spurious underflow */
  256. if((m & 2) && iy + (64 << 20) < ix) /* |y/x| < 0x1p-64, x < 0 */
  257. z = 0;
  258. else
  259. z = dmath_atan(dmath_fabs(y / x));
  260. switch(m) {
  261. case 0: return z; /* atan(+, +) */
  262. case 1: return -z; /* atan(-, +) */
  263. case 2: return pi - (z - pi_lo); /* atan(+, -) */
  264. default: return (z - pi_lo) - pi; /* atan(-, -) */
  265. }
  266. }
  267. // https://github.com/ziglang/zig/blob/master/lib/std/math/cbrt.zig
  268. //
  269. // cbrt is not required by IEEE 754 to be correctly rounded, so there is no
  270. // hardware instruction to lean on like `dmath_sqrt` does; this software
  271. // version is what gives the same bits on every platform.
  272. double dmath_cbrt(double x) {
  273. const uint32_t B1 = 715094163; /* (1023 - 1023 / 3 - 0.03306235651) * 2^20 */
  274. const uint32_t B2 = 696219795; /* (1023 - 1023 / 3 - 54 / 3 - 0.03306235651) * 2^20 */
  275. /* |1 / cbrt(x) - p(x)| < 2^-23.5 */
  276. const double P0 = 1.87595182427177009643;
  277. const double P1 = -1.88497979543377169875;
  278. const double P2 = 1.621429720105354466140;
  279. const double P3 = -0.758397934778766047437;
  280. const double P4 = 0.145996192886612446982;
  281. union ZigF64 ux = {.f = x};
  282. uint64_t u = ux.i;
  283. uint32_t hx = (uint32_t)(u >> 32) & 0x7FFFFFFF;
  284. /* cbrt(nan, inf) = itself */
  285. if(hx >= 0x7FF00000) return x + x;
  286. /* cbrt to ~5bits */
  287. if(hx < 0x00100000) {
  288. union ZigF64 us = {.f = x * 0x1.0p54};
  289. u = us.i;
  290. hx = (uint32_t)(u >> 32) & 0x7FFFFFFF;
  291. /* cbrt(+-0) = itself */
  292. if(hx == 0) return x;
  293. hx = hx / 3 + B2;
  294. } else {
  295. hx = hx / 3 + B1;
  296. }
  297. u &= 0x8000000000000000ULL;
  298. u |= (uint64_t)hx << 32;
  299. union ZigF64 ut = {.i = u};
  300. double t = ut.f;
  301. /* cbrt to 23 bits
  302. * cbrt(x) = t * cbrt(x / t^3) ~= t * P(t^3 / x) */
  303. double r = (t * t) * (t / x);
  304. t = t * ((P0 + r * (P1 + r * P2)) + ((r * r) * r) * (P3 + r * P4));
  305. /* Round t away from 0 to 23 bits */
  306. ut.f = t;
  307. ut.i = (ut.i + 0x80000000) & 0xFFFFFFFFC0000000ULL;
  308. t = ut.f;
  309. /* one step newton to 53 bits */
  310. double s = t * t;
  311. double q = x / s;
  312. double w = t + t;
  313. q = (q - t) / (w + q);
  314. return t + t * q;
  315. }