mathlib.red 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. MODULE MATHLIB; % Some useful mathematical functions.
  2. % Authors: W. Galway, M. L. Griss, A. C. Hearn. D. Irish, A. C. Norman,
  3. % D. Morrison.
  4. % ***** Constants declared as NewNam's *****
  5. % We can't use these long numbers in some Lisps because the reader can't
  6. % handle them (and it would truncate instead of round, anyway). These
  7. % are here for reference for implementation on other machines.
  8. % put('NumberPi,'NewNam,3.14159265358979324);
  9. % put('NumberPi!/2,'NewNam,1.57079632679489662);
  10. % put('NumberPi!/4,'NewNam,0.785398163397448310);
  11. deflist('((Number2Pi 6.2831853)
  12. (NumberPi 3.1415927)
  13. (NumberPi!/2 1.5707963)
  14. (NumberPi!/4 0.78539816)
  15. (Number3Pi!/4 2.3561945)
  16. (Number!-2Pi -6.2831853)
  17. (Number!-Pi -3.1415927)
  18. (Number!-Pi!/2 -1.5707963)
  19. (Number!-Pi!/4 -0.78539816)
  20. (SqrtTolerance 0.0000001)
  21. (NumberE 2.718281828)
  22. (NumberInverseE 0.36787944)
  23. (NaturalLog2 0.69314718)
  24. (NaturalLog10 2.3025851)
  25. (TrigPrecisionLimit 80)),
  26. 'newnam);
  27. % ***** Basic Functions *****
  28. symbolic procedure mod(M,N);
  29. % Returns M modulo N. Unlike remainder function, it returns
  30. % positive result in range 0..N-1, even if M is negative.
  31. % Needs more work for case of negative N.)
  32. (if result >= 0 then result else result + N)
  33. where result = remainder(M,N);
  34. symbolic procedure Floor x;
  35. % Returns the largest integer less than or equal to x
  36. % (i.e. the "greatest integer" function.)
  37. % Note the trickiness to compensate for fact that (unlike APL's
  38. % "FLOOR" function) FIX truncates towards zero.
  39. if fixp x then x
  40. else (if x = float n then n else if x >= 0 then n else n-1)
  41. where n = fix x;
  42. symbolic procedure Ceiling X;
  43. % Returns the smallest integer greater than or equal to X.
  44. % Note the trickiness to compensate for fact that (unlike APL's
  45. % "FLOOR" function) FIX truncates towards zero.
  46. if fixp X then X
  47. else (if x = float n then n else if x >= 0 then n+1 else n)
  48. where n = fix x;
  49. symbolic procedure Round X;
  50. % Rounds to the closest integer.
  51. % Kind of sloppy -- it's biased when the digit causing rounding is a
  52. % five. It's a bit weird with negative arguments, round(-2.5)= -2.
  53. if fixp X then X else floor(X+0.5);
  54. % ***** Trigonometric Functions *****
  55. % Trig functions are all in radians. The following few functions may
  56. % be used to convert to/from degrees, or degrees/minutes/seconds.
  57. symbolic procedure DegreesToRadians x;
  58. x*0.017453292; % 2*pi/360
  59. symbolic procedure RadiansToDegrees x;
  60. x*57.29578; % 360/(2*pi)
  61. symbolic procedure RadiansToDMS x;
  62. % Converts radians to a list of degrees, minutes, and seconds
  63. % (rounded, not truncated, to the nearest integer).
  64. begin scalar Degs,Mins;
  65. x := RadiansToDegrees x;
  66. Degs := fix x;
  67. x := 60*(x-Degs);
  68. Mins := fix x;
  69. return list(Degs,Mins, Round(60*(x-Mins)))
  70. end;
  71. symbolic procedure DMStoRadians(Degs,Mins,Sex);
  72. % Converts degrees, minutes, seconds to radians.
  73. % DegreesToRadians(Degs+Mins/60.0+Sex/3600.0)
  74. DegreesToRadians(Degs+Mins*0.016666667+Sex*0.00027777778);
  75. symbolic procedure sin x;
  76. % Accurate to about 6 decimal places, so long as the argument is
  77. % of commensurate precision. This will, of course, NOT be true for
  78. % large arguments, since they will be coming in with small precision.
  79. begin scalar neg;
  80. if minusp x then <<neg := T; x := -x>>;
  81. if x>TrigPrecisionLimit
  82. then ErrorPrintF
  83. "Possible loss of precision in computation of SIN";
  84. if x>NumberPi then x := x-Number2Pi*fix((x+NumberPi)/Number2Pi);
  85. if minusp x then <<neg := not neg; x := -x>>;
  86. if x > NumberPi!/2 then x := NumberPi-x;
  87. return if neg then -ScaledSine x else ScaledSine x
  88. end;
  89. symbolic procedure ScaledSine x;
  90. % assumes its argument is scaled to between 0 and pi/2.
  91. begin scalar xsqrd;
  92. xsqrd := x*x;
  93. return x*(1+xsqrd*(-0.16666667+xsqrd*(0.0083333315
  94. +xsqrd*(-0.0001984090
  95. +xsqrd*(0.0000027526-xsqrd*0.0000000239)))))
  96. end;
  97. symbolic procedure cos x;
  98. % Accurate to about 6 decimal places, so long as the argument is
  99. % of commensurate precision. This will, of course, NOT be true for
  100. % large arguments, since they will be coming in with small precision.
  101. <<if minusp x then x := - x;
  102. if x>TrigPrecisionLimit
  103. then ErrorPrintf
  104. "Possible loss of precision in computation of COS";
  105. if x>NumberPi then x := x-Number2Pi*fix((x+NumberPi)/Number2Pi);
  106. if minusp x then x := -x;
  107. if x>NumberPi!/2 then -ScaledCosine(NumberPi-x)
  108. else ScaledCosine x>>;
  109. symbolic procedure ScaledCosine x;
  110. % Expects its argument to be between 0 and pi/2.
  111. begin scalar xsqrd;
  112. xsqrd := x*x;
  113. return 1+xsqrd*(-0.5+xsqrd*(0.041666642+xsqrd*(-0.0013888397+
  114. xsqrd*(0.0000247609-xsqrd*0.0000002605))))
  115. end;
  116. symbolic procedure tan x;
  117. % Accurate to about 6 decimal places, so long as the argument is
  118. % of commensurate precision. This will, of course, NOT be true for
  119. % large arguments, since they will be coming in with small precision.
  120. begin scalar neg;
  121. if minusp x then <<neg := T; x := -x>>;
  122. if x>TrigPrecisionLimit
  123. then ErrorPrintF
  124. "Possible loss of precision in computation of TAN";
  125. if x>NumberPi!/2
  126. then x := x-NumberPi*fix((x+NumberPi!/2)/NumberPi);
  127. if minusp x then <<neg := not neg; x := -x>>;
  128. if x<NumberPi!/4 then x := ScaledTangent x
  129. else x := ScaledCotangent(-(x-numberpi!/2));
  130. return if neg then -x else x
  131. end;
  132. symbolic procedure cot x;
  133. % Accurate to about 6 decimal places, so long as the argument is
  134. % of commensurate precision. This will, of course, NOT be true for
  135. % large arguments, since they will be coming in with small precision.
  136. begin scalar neg;
  137. if minusp x then <<neg := T; x := -x>>;
  138. if x>NumberPi!/2
  139. then x := x-NumberPi*fix((x+NumberPi!/2)/NumberPi);
  140. if x>TrigPrecisionLimit
  141. then ErrorPrintF
  142. "Possible loss of precision in computation of COT";
  143. if minusp x then <<neg := not neg; x := -x>>;
  144. if x<NumberPi!/4 then x := ScaledCotangent x
  145. else x := ScaledTangent(-(x-numberpi!/2));
  146. return if neg then -x else x
  147. end;
  148. symbolic procedure ScaledTangent x;
  149. % Expects its argument to be between 0 and pi/4.
  150. (x*(1.0+xsqrd*(0.3333314+xsqrd*(0.1333924
  151. +xsqrd*(0.05337406 + xsqrd*(0.024565089
  152. +xsqrd*(0.002900525+xsqrd*0.0095168091)))))))
  153. where xsqrd = x*x;
  154. symbolic procedure ScaledCotangent x;
  155. % Expects its argument to be between 0 and pi/4.
  156. ((1.0-xsqrd*(0.33333334+xsqrd*(0.022222029+xsqrd*(0.0021177168 +
  157. xsqrd*(0.0002078504+xsqrd*0.0000262619)))))/x)
  158. where xsqrd = x*x;
  159. symbolic procedure sec x; 1.0/cos x;
  160. symbolic procedure csc x; 1.0/sin x;
  161. symbolic procedure sinD x; sin DegreesToRadians x;
  162. symbolic procedure cosD x; cos DegreesToRadians x;
  163. symbolic procedure tanD x; tan DegreesToRadians x;
  164. symbolic procedure cotD x; cot DegreesToRadians x;
  165. symbolic procedure secD x; sec DegreesToRadians x;
  166. symbolic procedure cscD x; csc DegreesToRadians x;
  167. symbolic procedure asin x;
  168. begin scalar neg;
  169. if minusp x then <<neg := T; x := -x>>;
  170. if x>1.0 then stderror list("Argument to ASIN too large:",x);
  171. return if neg then CheckedArcCosine x - NumberPi!/2
  172. else NumberPi!/2 - CheckedArcCosine x
  173. end;
  174. symbolic procedure acos x;
  175. begin scalar neg;
  176. if minusp x then <<neg := T; x := -x>>;
  177. if x>1.0 then stderror list("Argument to ACOS too large:",x);
  178. return if neg then NumberPi - CheckedArcCosine x
  179. else CheckedArcCosine x
  180. end;
  181. symbolic procedure CheckedArcCosine x;
  182. % Return cosine of a "checked number", assumes its argument is in
  183. % the range 0 <= x <= 1.
  184. sqrt(1.0-x)*(1.5707963+x*(-0.2145988+x*(0.088978987+x*(-0.050174305+
  185. x*(0.030891881+x*(-0.017088126+x*(0.0066700901
  186. -x*(0.0012624911))))))));
  187. symbolic procedure atan x;
  188. if minusp x
  189. then if x < -1.0 then Number!-Pi!/2 + CheckedArcTangent(-1.0/x)
  190. else -CheckedArcTangent(-x)
  191. else if x>1.0 then NumberPi!/2 - CheckedArcTangent(1.0/x)
  192. else CheckedArcTangent x;
  193. symbolic procedure acot x;
  194. if minusp x
  195. then if x<-1.0 then -CheckedArcTangent(-1.0/x)
  196. else Number!-Pi!/2 + CheckedArcTangent(-x)
  197. else if x>1.0 then CheckedArcTangent(1.0/x)
  198. else NumberPi!/2 - CheckedArcTangent x;
  199. symbolic procedure CheckedArcTangent x;
  200. (x*(1+xsqrd*(-0.33333145+xsqrd*(0.19993551+xsqrd*(-0.14208899+
  201. xsqrd*(0.10656264+xsqrd*(-0.07528964+xsqrd*(0.042909614+
  202. xsqrd*(-0.016165737+xsqrd*0.0028662257)))))))))
  203. where xsqrd = x*x;
  204. symbolic procedure asec x; acos(1.0/x);
  205. symbolic procedure acsc x; asin(1.0/x);
  206. symbolic procedure asinD x; RadiansToDegrees asin x;
  207. symbolic procedure acosD x; RadiansToDegrees acos x;
  208. symbolic procedure atanD x; RadiansToDegrees atan x;
  209. symbolic procedure acotD x; RadiansToDegrees acot x;
  210. symbolic procedure asecD x; RadiansToDegrees asec x;
  211. symbolic procedure acscD x; RadiansToDegrees acsc x;
  212. % ***** Hyperbolic Functions *****
  213. symbolic procedure sinh x; (exp x - exp(-x))/2.0;
  214. symbolic procedure cosh x; (exp x + exp(-x))/2.0;
  215. symbolic procedure tanh x; sinh x/cosh x;
  216. symbolic procedure csch x; 1/sinh x;
  217. symbolic procedure sech x; 1/cosh x;
  218. symbolic procedure coth x; 1/tanh x;
  219. symbolic procedure asinh x; log(x + sqrt(x**2+1.0));
  220. symbolic procedure acosh x;
  221. <<if x<0 then x := -x;
  222. if x<1 then stderror list("Argument to ACOSH too small:",x);
  223. log(x + sqrt(x**2-1.0))>>;
  224. symbolic procedure atanh x;
  225. begin scalar neg;
  226. if x<0 then <<neg := t; x := -x>>;
  227. if x>=1 then stderror list("Argument to ATANH too large:",x);
  228. x := log((1.0+x)/(1-x));
  229. return if neg then -x else x
  230. end;
  231. symbolic procedure acsch x;
  232. if x=0 then stderror "0 invalid argument to ACSCH"
  233. else log(y + sqrt(y**2+1)) where y = 1.0/x;
  234. symbolic procedure asech x;
  235. <<if x<0 then x := -x;
  236. if x>1 then stderror list("Argument to ASECH too large:",x);
  237. log(y + sqrt(y**2-1)) where y = 1.0/x>>;
  238. symbolic procedure acoth x;
  239. begin scalar neg;
  240. if x=0 then stderror "0 invalid argument to ACOTH"
  241. else if x<0 then <<neg := t; x := -x>>;
  242. if x<=1 then stderror list("Argument to ACOTH too small:",x);
  243. x := log((x+1.0)/(x-1));
  244. return if neg then -x else x
  245. end;
  246. % ***** Roots and Such *****
  247. symbolic procedure sqrt N;
  248. % Simple Newton-Raphson floating point square root calculator.
  249. % Not warranted against truncation errors, etc.
  250. begin integer scale; scalar answer;
  251. N:=FLOAT N;
  252. if N<0.0 then stderror list("SQRT given negative argument:",N);
  253. if zerop N then return N;
  254. % Scale argument to within 1e-10 to 1e+10;
  255. scale := 0;
  256. while N > 1.0E10 do <<scale := scale + 1; N := N * 1.0E-10>>;
  257. while N < 1.0E-10 do <<scale := scale - 1; N := N * 1.0E10>>;
  258. answer := if N>2.0 then (N+1)/2
  259. else if N<0.5 then 2/(N+1)
  260. else N;
  261. % Here's the heart of the algorithm.
  262. while abs(answer**2/N - 1.0) > SqrtTolerance do
  263. answer := 0.5*(answer+N/answer);
  264. return answer * 10.0**(5*scale)
  265. end;
  266. % ***** Logs and Exponentials *****
  267. symbolic procedure exp x;
  268. % Returns the exponential (ie, e**x) of its floatnum argument as
  269. % a flonum. The argument is scaled to
  270. % the interval -ln 2 to 0, and a Taylor series expansion
  271. % used (formula 4.2.45 on page 71 of Abramowitz and Stegun,
  272. % "Handbook of Mathematical Functions"). Note that little effort
  273. % has been expended to minimize truncation errors.
  274. % On many systems it will be appropriate to define a system-
  275. % specific EXP routine that does bother about rounding and that
  276. % understands the precision of the host floating point arithmetic;
  277. begin scalar N;
  278. N := ceiling(x / NaturalLog2);
  279. x := N * NaturalLog2 - x;
  280. return 2.0**N * (1.0+x*(-0.9999999995+x*(0.4999999206
  281. +x*(-0.1666653019+x*(0.0416573475+x*(-0.0083013598
  282. +x*(0.0013298820+x*(-0.0001413161))))))))
  283. end;
  284. symbolic procedure log x;
  285. % See Abramowitz and Stegun, page 69.
  286. if x<=0.0 then stderror list("LOG given non-positive argument:",x)
  287. else if x < 1.0 then -log(1.0/x)
  288. else
  289. % Find natural log of x > 1;
  290. begin scalar nextx, ipart; % ipart is the "integer part" of
  291. % the logarithm.
  292. ipart := 0;
  293. % Keep multiplying by 1/e until x is small enough, may want to
  294. % be more "efficient" if we ever use really big numbers.
  295. while (nextx := NumberInverseE * x) > 1.0 do
  296. <<x := nextx; ipart := ipart + 1>>;
  297. return ipart + if x < 2.0 then CheckedLogarithm x
  298. else 2.0 * CheckedLogarithm(sqrt(x))
  299. end;
  300. symbolic procedure CheckedLogarithm x;
  301. % Should have 1 <= x <= 2. (i.e. x = 1+y 0 <= y <= 1)
  302. <<x := x-1.0;
  303. x*(0.99999642+x*(-0.49987412+x*(0.33179903+x*(-0.24073381
  304. +x*(0.16765407+x*(-0.09532939
  305. +x*(0.036088494-x*0.0064535442)))))))>>;
  306. symbolic procedure log2 x; log x / NaturalLog2;
  307. symbolic procedure log10 x; log x / NaturalLog10;
  308. symbolic procedure factorial n; % simple factorial
  309. if n<0 or not fixp n
  310. then error(50,list(n,"invalid factorial argument"))
  311. else begin scalar m;
  312. m:=1;
  313. for i:=1:n do m:=m*i;
  314. return m;
  315. end;
  316. % Some functions from ALPHA_1 users
  317. symbolic procedure atan2d( y, x );
  318. radianstodegrees atan2( y, x );
  319. symbolic procedure atan2( y, x );
  320. <<x := float x;
  321. y := float y;
  322. if x = 0.0
  323. then if y>=0.0 then numberpi!/2 else numberpi+numberpi!/2
  324. else if x>=0.0 and y>=0.0 then atan(y/x) % first quadrant.
  325. else if x<0.0 and y>=0.0 then numberpi - atan(y/-x)
  326. % second quadrant.
  327. else if x<0.0 and y<0.0 then numberpi + atan(y/x)
  328. % third quadrant.
  329. else number2pi - atan(-y/x) % fourth quadrant.
  330. >>;
  331. symbolic procedure transfersign( s, val );
  332. % transfers the sign of s to val by returning abs(val) if s >= 0,
  333. % otherwise -abs(val).
  334. if s >= 0 then abs(val) else -abs(val);
  335. symbolic procedure dmstodegrees(degs,mins,sex);
  336. % converts degrees, minutes, seconds to degrees
  337. % degs+mins/60.0+sex/3600.0
  338. degs+mins*0.016666667+sex*0.00027777778;
  339. symbolic procedure degreestodms x;
  340. % converts degrees to a list of degrees, minutes, and seconds
  341. % (all integers, rounded, not truncated).
  342. begin scalar degs,mins;
  343. degs := fix x;
  344. x := 60*(x-degs);
  345. mins := fix x;
  346. return list(degs,mins, round(60*(x-mins)))
  347. end;
  348. endmodule;
  349. end;