1 /*
2 Title: Real number package.
3 Author: Dave Matthews, Cambridge University Computer Laboratory
4
5 Copyright (c) 2000
6 Cambridge University Technical Services Limited
7
8 Further work copyright David C.J. Matthews 2011, 2016-19
9
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License version 2.1 as published by the Free Software Foundation.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #elif defined(_WIN32)
28 #include "winconfig.h"
29 #else
30 #error "No configuration file"
31 #endif
32
33 #ifdef HAVE_IEEEFP_H
34 /* Other operating systems include "finite" in math.h, but Solaris doesn't? */
35 #include <ieeefp.h>
36 #endif
37
38 #ifdef HAVE_FPU_CONTROL_H
39 #include <fpu_control.h>
40 #endif
41
42 #ifdef HAVE_FENV_H
43 #include <fenv.h>
44 #endif
45
46 #ifdef HAVE_FLOAT_H
47 #include <float.h>
48 #endif
49
50 #ifdef HAVE_MATH_H
51 #include <math.h>
52 #endif
53
54 #ifdef HAVE_STDIO_H
55 #include <stdio.h>
56 #endif
57
58 #ifdef HAVE_STRING_H
59 #include <string.h>
60 #endif
61
62 #ifdef HAVE_ERRNO_H
63 #include <errno.h>
64 #endif
65
66 #ifdef HAVE_STDLIB_H
67 #include <stdlib.h>
68 #endif
69
70 #ifdef HAVE_STDINT_H
71 #include <stdint.h>
72 #endif
73
74 #ifdef HAVE_ASSERT_H
75 #include <assert.h>
76 #define ASSERT(x) assert(x)
77 #else
78 #define ASSERT(x)
79 #endif
80
81 #include <cmath> // Currently just for isnan.
82
83 #include "globals.h"
84 #include "run_time.h"
85 #include "reals.h"
86 #include "arb.h"
87 #include "sys.h"
88 #include "realconv.h"
89 #include "polystring.h"
90 #include "save_vec.h"
91 #include "rts_module.h"
92 #include "machine_dep.h"
93 #include "processes.h"
94 #include "rtsentry.h"
95
96 /*
97 The Standard Basis Library assumes IEEE representation for reals. Among other
98 things it does not permit equality on reals. That simplifies things
99 considerably since we don't have to worry about there being two different
100 representations of zero as 0 and ~0. We also don't need to check that the
101 result is finite since NaN is allowed as a result.
102 This code could do with being checked by someone who really understands
103 IEEE floating point arithmetic.
104 */
105
106 extern "C" {
107 POLYEXTERNALSYMBOL POLYUNSIGNED PolyRealBoxedToString(FirstArgument threadId, PolyWord arg, PolyWord mode, PolyWord digits);
108 POLYEXTERNALSYMBOL POLYUNSIGNED PolyRealGeneral(FirstArgument threadId, PolyWord code, PolyWord arg);
109 POLYEXTERNALSYMBOL POLYUNSIGNED PolyRealBoxedFromString(FirstArgument threadId, PolyWord str);
110 POLYEXTERNALSYMBOL POLYUNSIGNED PolyRealBoxedToLongInt(FirstArgument threadId, PolyWord arg);
111 POLYEXTERNALSYMBOL double PolyRealSqrt(double arg);
112 POLYEXTERNALSYMBOL double PolyRealSin(double arg);
113 POLYEXTERNALSYMBOL double PolyRealCos(double arg);
114 POLYEXTERNALSYMBOL double PolyRealArctan(double arg);
115 POLYEXTERNALSYMBOL double PolyRealExp(double arg);
116 POLYEXTERNALSYMBOL double PolyRealLog(double arg);
117 POLYEXTERNALSYMBOL double PolyRealTan(double arg);
118 POLYEXTERNALSYMBOL double PolyRealArcSin(double arg);
119 POLYEXTERNALSYMBOL double PolyRealArcCos(double arg);
120 POLYEXTERNALSYMBOL double PolyRealLog10(double arg);
121 POLYEXTERNALSYMBOL double PolyRealSinh(double arg);
122 POLYEXTERNALSYMBOL double PolyRealCosh(double arg);
123 POLYEXTERNALSYMBOL double PolyRealTanh(double arg);
124 POLYEXTERNALSYMBOL double PolyRealFloor(double arg);
125 POLYEXTERNALSYMBOL double PolyRealCeil(double arg);
126 POLYEXTERNALSYMBOL double PolyRealTrunc(double arg);
127 POLYEXTERNALSYMBOL double PolyRealRound(double arg);
128 POLYEXTERNALSYMBOL double PolyRealRem(double arg1, double arg2);
129 POLYEXTERNALSYMBOL double PolyFloatArbitraryPrecision(PolyWord arg);
130 POLYEXTERNALSYMBOL POLYSIGNED PolyGetRoundingMode(PolyWord);
131 POLYEXTERNALSYMBOL POLYSIGNED PolySetRoundingMode(PolyWord);
132 POLYEXTERNALSYMBOL POLYUNSIGNED PolyRealSize(PolyWord);
133 POLYEXTERNALSYMBOL double PolyRealAtan2(double arg1, double arg2);
134 POLYEXTERNALSYMBOL double PolyRealPow(double arg1, double arg2);
135 POLYEXTERNALSYMBOL double PolyRealCopySign(double arg1, double arg2);
136 POLYEXTERNALSYMBOL double PolyRealNextAfter(double arg1, double arg2);
137 POLYEXTERNALSYMBOL double PolyRealLdexp(double arg1, PolyWord arg2);
138 POLYEXTERNALSYMBOL POLYUNSIGNED PolyRealFrexp(FirstArgument threadId, PolyWord arg);
139 POLYEXTERNALSYMBOL float PolyRealFSqrt(float arg);
140 POLYEXTERNALSYMBOL float PolyRealFSin(float arg);
141 POLYEXTERNALSYMBOL float PolyRealFCos(float arg);
142 POLYEXTERNALSYMBOL float PolyRealFArctan(float arg);
143 POLYEXTERNALSYMBOL float PolyRealFExp(float arg);
144 POLYEXTERNALSYMBOL float PolyRealFLog(float arg);
145 POLYEXTERNALSYMBOL float PolyRealFTan(float arg);
146 POLYEXTERNALSYMBOL float PolyRealFArcSin(float arg);
147 POLYEXTERNALSYMBOL float PolyRealFArcCos(float arg);
148 POLYEXTERNALSYMBOL float PolyRealFLog10(float arg);
149 POLYEXTERNALSYMBOL float PolyRealFSinh(float arg);
150 POLYEXTERNALSYMBOL float PolyRealFCosh(float arg);
151 POLYEXTERNALSYMBOL float PolyRealFTanh(float arg);
152 POLYEXTERNALSYMBOL float PolyRealFAtan2(float arg1, float arg2);
153 POLYEXTERNALSYMBOL float PolyRealFPow(float arg1, float arg2);
154 POLYEXTERNALSYMBOL float PolyRealFCopySign(float arg1, float arg2);
155 POLYEXTERNALSYMBOL float PolyRealFFloor(float arg);
156 POLYEXTERNALSYMBOL float PolyRealFCeil(float arg);
157 POLYEXTERNALSYMBOL float PolyRealFTrunc(float arg);
158 POLYEXTERNALSYMBOL float PolyRealFRound(float arg);
159 POLYEXTERNALSYMBOL float PolyRealFRem(float arg1, float arg2);
160 POLYEXTERNALSYMBOL float PolyRealFNextAfter(float arg1, float arg2);
161 }
162
163 static Handle Real_strc(TaskData *mdTaskData, Handle hDigits, Handle hMode, Handle arg);
164 static Handle Real_convc(TaskData *mdTaskData, Handle str);
165
166
167 // Positive and negative infinities and (positive) NaN.
168 double posInf, negInf, notANumber;
169 float posInfF, negInfF, notANumberF;
170
171 /* Real numbers are represented by the address of the value. */
172 #define DBLE sizeof(double)/sizeof(POLYUNSIGNED)
173
174 union db { double dble; POLYUNSIGNED words[DBLE]; };
175
real_arg(Handle x)176 double real_arg(Handle x)
177 {
178 union db r_arg_x;
179 for (unsigned i = 0; i < DBLE; i++)
180 {
181 r_arg_x.words[i] = x->WordP()->Get(i).AsUnsigned();
182 }
183 return r_arg_x.dble;
184 }
185
real_result(TaskData * mdTaskData,double x)186 Handle real_result(TaskData *mdTaskData, double x)
187 {
188 union db argx;
189
190 argx.dble = x;
191
192 PolyObject *v = alloc(mdTaskData, DBLE, F_BYTE_OBJ);
193 /* Copy as words in case the alignment is wrong. */
194 for(unsigned i = 0; i < DBLE; i++)
195 {
196 v->Set(i, PolyWord::FromUnsigned(argx.words[i]));
197 }
198 return mdTaskData->saveVec.push(v);
199 }
200
201 // We're using float for Real32 so it needs to be 32-bits.
202 // Assume that's true for the moment.
203 #if (SIZEOF_FLOAT != 4)
204 #error "Float is not 32-bits. Please report this"
205 #endif
206
207 union flt { float fl; int32_t i; };
208
209 #if (SIZEOF_FLOAT < SIZEOF_POLYWORD)
210
211 // Typically for 64-bit mode. Use a tagged representation.
212 // The code-generator on the X86/64 assumes the float is in the
213 // high order word.
214 #define FLT_SHIFT ((SIZEOF_POLYWORD-SIZEOF_FLOAT)*8)
float_arg(Handle x)215 float float_arg(Handle x)
216 {
217 union flt argx;
218 argx.i = x->Word().AsSigned() >> FLT_SHIFT;
219 return argx.fl;
220 }
221
float_result(TaskData * mdTaskData,float x)222 Handle float_result(TaskData *mdTaskData, float x)
223 {
224 union flt argx;
225 argx.fl = x;
226 return mdTaskData->saveVec.push(PolyWord::FromSigned(((POLYSIGNED)argx.i << FLT_SHIFT) + 1));
227 }
228 #else
229 // Typically for 32-bit mode. Use a boxed representation.
float_arg(Handle x)230 float float_arg(Handle x)
231 {
232 union flt argx;
233 argx.i = (int32_t)x->WordP()->Get(0).AsSigned();
234 return argx.fl;
235 }
236
float_result(TaskData * mdTaskData,float x)237 Handle float_result(TaskData *mdTaskData, float x)
238 {
239 union flt argx;
240 argx.fl = x;
241
242 PolyObject *v = alloc(mdTaskData, 1, F_BYTE_OBJ);
243 v->Set(0, PolyWord::FromSigned(argx.i));
244 return mdTaskData->saveVec.push(v);
245 }
246
247 #endif
248
PolyFloatArbitraryPrecision(PolyWord arg)249 POLYEXTERNALSYMBOL double PolyFloatArbitraryPrecision(PolyWord arg)
250 {
251 return get_arbitrary_precision_as_real(arg);
252 }
253
254 // Convert a boxed real to a long precision int.
PolyRealBoxedToLongInt(FirstArgument threadId,PolyWord arg)255 POLYUNSIGNED PolyRealBoxedToLongInt(FirstArgument threadId, PolyWord arg)
256 {
257 TaskData *taskData = TaskData::FindTaskForId(threadId);
258 ASSERT(taskData != 0);
259 taskData->PreRTSCall();
260 Handle reset = taskData->saveVec.mark();
261 Handle pushedArg = taskData->saveVec.push(arg);
262
263 double dx = real_arg(pushedArg);
264 int64_t i = (int64_t)dx;
265 Handle result = Make_arbitrary_precision(taskData, i);
266
267 taskData->saveVec.reset(reset);
268 taskData->PostRTSCall();
269 if (result == 0) return TAGGED(0).AsUnsigned();
270 else return result->Word().AsUnsigned();
271 }
272
273 // RTS call for square-root.
PolyRealSqrt(double arg)274 double PolyRealSqrt(double arg)
275 {
276 return sqrt(arg);
277 }
278
279 // RTS call for sine.
PolyRealSin(double arg)280 double PolyRealSin(double arg)
281 {
282 return sin(arg);
283 }
284
285 // RTS call for cosine.
PolyRealCos(double arg)286 double PolyRealCos(double arg)
287 {
288 return cos(arg);
289 }
290
291 // RTS call for arctan.
PolyRealArctan(double arg)292 double PolyRealArctan(double arg)
293 {
294 return atan(arg);
295 }
296
297 // RTS call for exp.
PolyRealExp(double arg)298 double PolyRealExp(double arg)
299 {
300 return exp(arg);
301 }
302
303 // RTS call for ln.
PolyRealLog(double arg)304 double PolyRealLog(double arg)
305 {
306 // Make sure the result conforms to the definition.
307 // If the argument is a Nan each of the first two tests will fail.
308 if (arg > 0.0)
309 return log(arg);
310 else if (arg == 0.0) // x may be +0.0 or -0.0
311 return negInf; // -infinity.
312 else return notANumber;
313 }
314
315 // These were handled by the general dispatch function
PolyRealTan(double arg)316 double PolyRealTan(double arg)
317 {
318 return tan(arg);
319 }
320
PolyRealArcSin(double arg)321 double PolyRealArcSin(double arg)
322 {
323 if (arg >= -1.0 && arg <= 1.0)
324 return asin(arg);
325 else return notANumber;
326 }
327
PolyRealArcCos(double arg)328 double PolyRealArcCos(double arg)
329 {
330 if (arg >= -1.0 && arg <= 1.0)
331 return acos(arg);
332 else return notANumber;
333 }
334
PolyRealLog10(double arg)335 double PolyRealLog10(double arg)
336 {
337 // Make sure the result conforms to the definition.
338 // If the argument is a Nan each of the first two tests will fail.
339 if (arg > 0.0)
340 return log10(arg);
341 else if (arg == 0.0) // x may be +0.0 or -0.0
342 return negInf; // -infinity.
343 else return notANumber;
344 }
345
PolyRealSinh(double arg)346 double PolyRealSinh(double arg)
347 {
348 return sinh(arg);
349 }
350
PolyRealCosh(double arg)351 double PolyRealCosh(double arg)
352 {
353 return cosh(arg);
354 }
355
PolyRealTanh(double arg)356 double PolyRealTanh(double arg)
357 {
358 return tanh(arg);
359 }
360
PolyRealFloor(double arg)361 double PolyRealFloor(double arg)
362 {
363 return floor(arg);
364 }
365
PolyRealCeil(double arg)366 double PolyRealCeil(double arg)
367 {
368 return ceil(arg);
369 }
370
PolyRealTrunc(double arg)371 double PolyRealTrunc(double arg)
372 {
373 // Truncate towards zero
374 if (arg >= 0.0) return floor(arg);
375 else return ceil(arg);
376 }
377
PolyRealRound(double arg)378 double PolyRealRound(double arg)
379 {
380 // Round to nearest integral value.
381 double drem = fmod(arg, 2.0);
382 if (drem == 0.5 || drem == -1.5)
383 // If the value was exactly positive even + 0.5 or
384 // negative odd -0.5 round it down, otherwise round it up.
385 return ceil(arg-0.5);
386 else return floor(arg+0.5);
387 }
388
PolyRealRem(double arg1,double arg2)389 double PolyRealRem(double arg1, double arg2)
390 {
391 return fmod(arg1, arg2);
392 }
393
PolyRealAtan2(double arg1,double arg2)394 double PolyRealAtan2(double arg1, double arg2)
395 {
396 return atan2(arg1, arg2);
397 }
398
PolyRealPow(double x,double y)399 double PolyRealPow(double x, double y)
400 {
401 /* Some of the special cases are defined and don't seem to match
402 the C pow function (at least as implemented in MS C). */
403 /* Maybe handle all this in ML? */
404 if (std::isnan(x))
405 {
406 if (y == 0.0) return 1.0;
407 else return notANumber;
408 }
409 else if (std::isnan(y)) return y; /* i.e. nan. */
410 else if (x == 0.0 && y < 0.0)
411 {
412 /* This case is not handled correctly in Solaris. It always
413 returns -infinity. */
414 int iy = (int)floor(y);
415 /* If x is -0.0 and y is an odd integer the result is -infinity. */
416 if (copysign(1.0, x) < 0.0 && (double)iy == y && (iy & 1))
417 return negInf; /* -infinity. */
418 else return posInf; /* +infinity. */
419 }
420 return pow(x, y);
421 }
422
PolyRealCopySign(double arg1,double arg2)423 double PolyRealCopySign(double arg1, double arg2)
424 {
425 return copysign(arg1, arg2);
426 }
427
PolyRealNextAfter(double arg1,double arg2)428 double PolyRealNextAfter(double arg1, double arg2)
429 {
430 return nextafter(arg1, arg2);
431 }
432
PolyRealLdexp(double arg1,PolyWord arg2)433 double PolyRealLdexp(double arg1, PolyWord arg2)
434 {
435 POLYSIGNED exponent = arg2.UnTagged();
436 #if (SIZEOF_POLYWORD > SIZEOF_INT)
437 // We've already checked for arbitrary precision values where necessary and
438 // for zero and non-finite mantissa. Check the exponent fits in an int.
439 if (exponent > 2 * DBL_MAX_EXP) return copysign(INFINITY, arg1);
440 if (exponent < -2 * DBL_MAX_EXP) return copysign(0.0, arg1);
441 #endif
442 return ldexp(arg1, (int)exponent);
443 }
444
445 // Return the normalised fraction and the exponent.
PolyRealFrexp(FirstArgument threadId,PolyWord arg)446 POLYUNSIGNED PolyRealFrexp(FirstArgument threadId, PolyWord arg)
447 {
448 TaskData *taskData = TaskData::FindTaskForId(threadId);
449 ASSERT(taskData != 0);
450 taskData->PreRTSCall();
451 Handle reset = taskData->saveVec.mark();
452 Handle pushedArg = taskData->saveVec.push(arg);
453 Handle result = 0;
454
455 try {
456 int exp = 0; // The value of exp is not always defined.
457 Handle mantH = real_result(taskData, frexp(real_arg(pushedArg), &exp));
458 // Allocate a pair for the result
459 result = alloc_and_save(taskData, 2);
460
461 result->WordP()->Set(0, TAGGED(exp));
462 result->WordP()->Set(1, mantH->Word());
463 }
464 catch (...) {} // If an ML exception is raised
465
466 taskData->saveVec.reset(reset);
467 taskData->PostRTSCall();
468 if (result == 0) return TAGGED(0).AsUnsigned();
469 else return result->Word().AsUnsigned();
470 }
471
472 // RTS call for square-root.
PolyRealFSqrt(float arg)473 float PolyRealFSqrt(float arg)
474 {
475 return sqrtf(arg);
476 }
477
478 // RTS call for sine.
PolyRealFSin(float arg)479 float PolyRealFSin(float arg)
480 {
481 return sinf(arg);
482 }
483
484 // RTS call for cosine.
PolyRealFCos(float arg)485 float PolyRealFCos(float arg)
486 {
487 return cosf(arg);
488 }
489
490 // RTS call for arctan.
PolyRealFArctan(float arg)491 float PolyRealFArctan(float arg)
492 {
493 return atanf(arg);
494 }
495
496 // RTS call for exp.
PolyRealFExp(float arg)497 float PolyRealFExp(float arg)
498 {
499 return expf(arg);
500 }
501
502 // RTS call for ln.
PolyRealFLog(float arg)503 float PolyRealFLog(float arg)
504 {
505 // Make sure the result conforms to the definition.
506 // If the argument is a Nan each of the first two tests will fail.
507 if (arg > 0.0)
508 return logf(arg);
509 else if (arg == 0.0) // x may be +0.0 or -0.0
510 return negInfF; // -infinity.
511 else return notANumberF;
512 }
513
PolyRealFTan(float arg)514 float PolyRealFTan(float arg)
515 {
516 return tanf(arg);
517 }
518
PolyRealFArcSin(float arg)519 float PolyRealFArcSin(float arg)
520 {
521 if (arg >= -1.0 && arg <= 1.0)
522 return asinf(arg);
523 else return notANumberF;
524 }
525
PolyRealFArcCos(float arg)526 float PolyRealFArcCos(float arg)
527 {
528 if (arg >= -1.0 && arg <= 1.0)
529 return acosf(arg);
530 else return notANumberF;
531 }
532
PolyRealFLog10(float arg)533 float PolyRealFLog10(float arg)
534 {
535 // Make sure the result conforms to the definition.
536 // If the argument is a Nan each of the first two tests will fail.
537 if (arg > 0.0)
538 return log10f(arg);
539 else if (arg == 0.0) // x may be +0.0 or -0.0
540 return negInfF; // -infinity.
541 else return notANumberF;
542 }
543
PolyRealFSinh(float arg)544 float PolyRealFSinh(float arg)
545 {
546 return sinhf(arg);
547 }
548
PolyRealFCosh(float arg)549 float PolyRealFCosh(float arg)
550 {
551 return coshf(arg);
552 }
553
PolyRealFTanh(float arg)554 float PolyRealFTanh(float arg)
555 {
556 return tanhf(arg);
557 }
558
PolyRealFAtan2(float arg1,float arg2)559 float PolyRealFAtan2(float arg1, float arg2)
560 {
561 return atan2f(arg1, arg2);
562 }
563
PolyRealFPow(float x,float y)564 float PolyRealFPow(float x, float y)
565 {
566 /* Some of the special cases are defined and don't seem to match
567 the C pow function (at least as implemented in MS C). */
568 /* Maybe handle all this in ML? */
569 if (std::isnan(x))
570 {
571 if (y == 0.0) return 1.0;
572 else return notANumberF;
573 }
574 else if (std::isnan(y)) return y; /* i.e. nan. */
575 else if (x == 0.0 && y < 0.0)
576 {
577 /* This case is not handled correctly in Solaris. It always
578 returns -infinity. */
579 int iy = (int)floorf(y);
580 /* If x is -0.0 and y is an odd integer the result is -infinity. */
581 if (copysign(1.0, x) < 0.0 && (float)iy == y && (iy & 1))
582 return negInfF; /* -infinity. */
583 else return posInfF; /* +infinity. */
584 }
585 return powf(x, y);
586 }
587
PolyRealFFloor(float arg)588 float PolyRealFFloor(float arg)
589 {
590 return floorf(arg);
591 }
592
PolyRealFCeil(float arg)593 float PolyRealFCeil(float arg)
594 {
595 return ceilf(arg);
596 }
597
PolyRealFTrunc(float arg)598 float PolyRealFTrunc(float arg)
599 {
600 // Truncate towards zero
601 if (arg >= 0.0) return floorf(arg);
602 else return ceilf(arg);
603 }
604
PolyRealFRound(float arg)605 float PolyRealFRound(float arg)
606 {
607 // Round to nearest integral value.
608 float drem = fmodf(arg, 2.0);
609 if (drem == 0.5 || drem == -1.5)
610 // If the value was exactly positive even + 0.5 or
611 // negative odd -0.5 round it down, otherwise round it up.
612 return ceilf(arg - 0.5f);
613 else return floorf(arg + 0.5f);
614 }
615
PolyRealFRem(float arg1,float arg2)616 float PolyRealFRem(float arg1, float arg2)
617 {
618 return fmodf(arg1, arg2);
619 }
620
PolyRealFCopySign(float arg1,float arg2)621 float PolyRealFCopySign(float arg1, float arg2)
622 {
623 return copysignf(arg1, arg2);
624 }
625
PolyRealFNextAfter(float arg1,float arg2)626 float PolyRealFNextAfter(float arg1, float arg2)
627 {
628 return nextafterf(arg1, arg2);
629 }
630
631 /* CALL_IO1(Real_conv, REF, NOIND) */
Real_convc(TaskData * mdTaskData,Handle str)632 Handle Real_convc(TaskData *mdTaskData, Handle str) /* string to real */
633 {
634 double result;
635 int i;
636 char *finish;
637 TempCString string_buffer(Poly_string_to_C_alloc(str->Word()));
638
639 /* Scan the string turning '~' into '-' */
640 for(i = 0; string_buffer[i] != '\0'; i ++)
641 {
642 if (string_buffer[i] == '~') string_buffer[i] = '-';
643 }
644
645 /* Now convert it */
646 #ifdef HAVE_STRTOD
647 result = strtod(string_buffer, &finish);
648 #else
649 result = poly_strtod(string_buffer, &finish);
650 #endif
651 // We no longer detect overflow and underflow and instead return
652 // (signed) zeros for underflow and (signed) infinities for overflow.
653 if (*finish != '\0') raise_exception_string(mdTaskData, EXC_conversion, "");
654
655 return real_result(mdTaskData, result);
656 }/* Real_conv */
657
658 // Convert a string to a boxed real. This should really return an unboxed real.
PolyRealBoxedFromString(FirstArgument threadId,PolyWord str)659 POLYUNSIGNED PolyRealBoxedFromString(FirstArgument threadId, PolyWord str)
660 {
661 TaskData *taskData = TaskData::FindTaskForId(threadId);
662 ASSERT(taskData != 0);
663 taskData->PreRTSCall();
664 Handle reset = taskData->saveVec.mark();
665 Handle pushedString = taskData->saveVec.push(str);
666 Handle result = 0;
667
668 try {
669 result = Real_convc(taskData, pushedString);
670 } catch (...) { } // If an ML exception is raised
671
672 taskData->saveVec.reset(reset);
673 taskData->PostRTSCall();
674 if (result == 0) return TAGGED(0).AsUnsigned();
675 else return result->Word().AsUnsigned();
676 }
677
678 #if defined(__SOFTFP__)
679 // soft-float lacks proper rounding mode support
680 // While some systems will support fegetround/fesetround, it will have no
681 // effect on the actual rounding performed, as the software implementation only
682 // ever rounds to nearest.
getrounding()683 int getrounding()
684 {
685 return POLY_ROUND_TONEAREST;
686 }
687
setrounding(int rounding)688 int setrounding(int rounding)
689 {
690 switch (rounding)
691 {
692 case POLY_ROUND_TONEAREST: return 0; // The only mode supported
693 }
694 return -1; // Error - unsupported
695 }
696
697 // It would be nice to be able to use autoconf to test for these as functions
698 // but they are frequently inlined
699 #elif defined(HAVE_FENV_H)
700 // C99 version. This is becoming the most common.
getrounding()701 int getrounding()
702 {
703 switch (fegetround())
704 {
705 case FE_TONEAREST: return POLY_ROUND_TONEAREST;
706 #ifndef HOSTARCHITECTURE_SH
707 case FE_DOWNWARD: return POLY_ROUND_DOWNWARD;
708 case FE_UPWARD: return POLY_ROUND_UPWARD;
709 #endif
710 case FE_TOWARDZERO: return POLY_ROUND_TOZERO;
711 }
712 return POLY_ROUND_TONEAREST;
713 }
714
setrounding(int rounding)715 int setrounding(int rounding)
716 {
717 switch (rounding)
718 {
719 case POLY_ROUND_TONEAREST: fesetround(FE_TONEAREST); return 0; // Choose nearest
720 #ifndef HOSTARCHITECTURE_SH
721 case POLY_ROUND_DOWNWARD: fesetround(FE_DOWNWARD); return 0; // Towards negative infinity
722 case POLY_ROUND_UPWARD: fesetround(FE_UPWARD); return 0; // Towards positive infinity
723 #endif
724 case POLY_ROUND_TOZERO: fesetround(FE_TOWARDZERO); return 0; // Truncate towards zero
725 default: return -1;
726 }
727 }
728
729 #elif (defined(HAVE_IEEEFP_H) && ! defined(__CYGWIN__))
730 // Older FreeBSD. Cygwin has the ieeefp.h header but not the functions!
getrounding()731 int getrounding()
732 {
733 switch (fpgetround())
734 {
735 case FP_RN: return POLY_ROUND_TONEAREST;
736 case FP_RM: return POLY_ROUND_DOWNWARD;
737 case FP_RP: return POLY_ROUND_UPWARD;
738 case FP_RZ: return POLY_ROUND_TOZERO;
739 default: return POLY_ROUND_TONEAREST; /* Shouldn't happen. */
740 }
741 }
742
setrounding(int rounding)743 int setrounding(int rounding)
744 {
745 switch (rounding)
746 {
747 case POLY_ROUND_TONEAREST: fpsetround(FP_RN); break; /* Choose nearest */
748 case POLY_ROUND_DOWNWARD: fpsetround(FP_RM); break; /* Towards negative infinity */
749 case POLY_ROUND_UPWARD: fpsetround(FP_RP); break; /* Towards positive infinity */
750 case POLY_ROUND_TOZERO: fpsetround(FP_RZ); break; /* Truncate towards zero */
751 }
752 return 0
753 }
754
755 #elif defined(_WIN32)
756 // Windows version
getrounding()757 int getrounding()
758 {
759 switch (_controlfp(0,0) & _MCW_RC)
760 {
761 case _RC_NEAR: return POLY_ROUND_TONEAREST;
762 case _RC_DOWN: return POLY_ROUND_DOWNWARD;
763 case _RC_UP: return POLY_ROUND_UPWARD;
764 case _RC_CHOP: return POLY_ROUND_TOZERO;
765 }
766 return POLY_ROUND_TONEAREST;
767 }
768
setrounding(int rounding)769 int setrounding(int rounding)
770 {
771 switch (rounding)
772 {
773 case POLY_ROUND_TONEAREST: _controlfp(_RC_NEAR, _MCW_RC); return 0; // Choose nearest
774 case POLY_ROUND_DOWNWARD: _controlfp(_RC_DOWN, _MCW_RC); return 0; // Towards negative infinity
775 case POLY_ROUND_UPWARD: _controlfp(_RC_UP, _MCW_RC); return 0; // Towards positive infinity
776 case POLY_ROUND_TOZERO: _controlfp(_RC_CHOP, _MCW_RC); return 0; // Truncate towards zero
777 }
778 return -1;
779 }
780
781 #elif defined(_FPU_GETCW) && defined(_FPU_SETCW)
782 // Older Linux version
getrounding()783 int getrounding()
784 {
785 fpu_control_t ctrl;
786 _FPU_GETCW(ctrl);
787 switch (ctrl & _FPU_RC_ZERO)
788 {
789 case _FPU_RC_NEAREST: return POLY_ROUND_TONEAREST;
790 case _FPU_RC_DOWN: return POLY_ROUND_DOWNWARD;
791 case _FPU_RC_UP: return POLY_ROUND_UPWARD;
792 case _FPU_RC_ZERO: return POLY_ROUND_TOZERO;
793 }
794 return POLY_ROUND_TONEAREST; /* Never reached but this avoids warning message. */
795 }
796
setrounding(int rounding)797 int setrounding(int rounding)
798 {
799 fpu_control_t ctrl;
800 _FPU_GETCW(ctrl);
801 ctrl &= ~_FPU_RC_ZERO; /* Mask off any existing rounding. */
802 switch (rounding)
803 {
804 case POLY_ROUND_TONEAREST: ctrl |= _FPU_RC_NEAREST;
805 case POLY_ROUND_DOWNWARD: ctrl |= _FPU_RC_DOWN;
806 case POLY_ROUND_UPWARD: ctrl |= _FPU_RC_UP;
807 case POLY_ROUND_TOZERO: ctrl |= _FPU_RC_ZERO;
808 }
809 _FPU_SETCW(ctrl);
810 return 0;
811 }
812 #else
813 // Give up. Assume that we only support TO_NEAREST
getrounding()814 int getrounding()
815 {
816 return POLY_ROUND_TONEAREST;
817 }
818
setrounding(int rounding)819 int setrounding(int rounding)
820 {
821 if (rounding == POLY_ROUND_TONEAREST)
822 return 0;
823 else return -1;
824 }
825 #endif
826
PolyGetRoundingMode(PolyWord)827 POLYSIGNED PolyGetRoundingMode(PolyWord)
828 {
829 // Get the rounding and turn the result into a tagged integer.
830 return TAGGED(getrounding()).AsSigned();
831 }
832
PolySetRoundingMode(PolyWord arg)833 POLYSIGNED PolySetRoundingMode(PolyWord arg)
834 {
835 return TAGGED(setrounding((int)arg.UnTagged())).AsSigned();
836 }
837
Real_strc(TaskData * mdTaskData,Handle hDigits,Handle hMode,Handle arg)838 Handle Real_strc(TaskData *mdTaskData, Handle hDigits, Handle hMode, Handle arg)
839 {
840 double dx = real_arg(arg);
841 int decpt, sign;
842 int mode = get_C_int(mdTaskData, hMode->Word());
843 int digits = get_C_int(mdTaskData, hDigits->Word());
844 /* Compute the shortest string which gives the required value. */
845 /* */
846 char *chars = poly_dtoa(dx, mode, digits, &decpt, &sign, NULL);
847 /* We have to be careful in case an allocation causes a
848 garbage collection. */
849 PolyWord pStr = C_string_to_Poly(mdTaskData, chars);
850 poly_freedtoa(chars);
851 Handle ppStr = mdTaskData->saveVec.push(pStr);
852 /* Allocate a triple for the results. */
853 PolyObject *result = alloc(mdTaskData, 3);
854 result->Set(0, ppStr->Word());
855 result->Set(1, TAGGED(decpt));
856 result->Set(2, TAGGED(sign));
857 return mdTaskData->saveVec.push(result);
858 }
859
860 // Convert boxed real to string. This should be changed to use an unboxed real argument.
PolyRealBoxedToString(FirstArgument threadId,PolyWord arg,PolyWord mode,PolyWord digits)861 POLYUNSIGNED PolyRealBoxedToString(FirstArgument threadId, PolyWord arg, PolyWord mode, PolyWord digits)
862 {
863 TaskData *taskData = TaskData::FindTaskForId(threadId);
864 ASSERT(taskData != 0);
865 taskData->PreRTSCall();
866 Handle reset = taskData->saveVec.mark();
867 Handle pushedArg = taskData->saveVec.push(arg);
868 Handle pushedMode = taskData->saveVec.push(mode);
869 Handle pushedDigits = taskData->saveVec.push(digits);
870 Handle result = 0;
871
872 try {
873 result = Real_strc(taskData, pushedDigits, pushedMode, pushedArg);
874 } catch (...) { } // Can this raise an exception?
875
876 taskData->saveVec.reset(reset);
877 taskData->PostRTSCall();
878 if (result == 0) return TAGGED(0).AsUnsigned();
879 else return result->Word().AsUnsigned();
880 }
881
882 // This used to be used for all the functions. It now only contains calls
883 // used when the Real structure is defined to get the values of constants.
Real_dispatchc(TaskData * mdTaskData,Handle args,Handle code)884 static Handle Real_dispatchc(TaskData *mdTaskData, Handle args, Handle code)
885 {
886 unsigned c = get_C_unsigned(mdTaskData, code->Word());
887 switch (c)
888 {
889 /* Floating point representation queries. */
890 #ifdef _DBL_RADIX
891 case 11: /* Value of radix */ return mdTaskData->saveVec.push(TAGGED(_DBL_RADIX));
892 #else
893 case 11: /* Value of radix */ return mdTaskData->saveVec.push(TAGGED(FLT_RADIX));
894 #endif
895 case 12: /* Value of precision */ return mdTaskData->saveVec.push(TAGGED(DBL_MANT_DIG));
896 case 13: /* Maximum number */ return real_result(mdTaskData, DBL_MAX);
897 case 14: /* Minimum normalised number. */
898 return real_result(mdTaskData, DBL_MIN);
899
900 case 15: // Minimum number.
901 #ifdef DBL_TRUE_MIN
902 return real_result(mdTaskData, DBL_TRUE_MIN);
903 #else
904 return real_result(mdTaskData, DBL_MIN*DBL_EPSILON);
905 #endif
906
907 // Constants for float (Real32.real)
908 case 30: /* Value of radix */ return mdTaskData->saveVec.push(TAGGED(FLT_RADIX));
909 case 31: /* Value of precision */ return mdTaskData->saveVec.push(TAGGED(FLT_MANT_DIG));
910 case 32: /* Maximum number */ return float_result(mdTaskData, FLT_MAX);
911 case 33: /* Minimum normalised number. */
912 return float_result(mdTaskData, FLT_MIN);
913 case 34: // Minimum number.
914 #ifdef FLT_TRUE_MIN
915 return float_result(mdTaskData, FLT_TRUE_MIN);
916 #else
917 return float_result(mdTaskData, FLT_MIN*FLT_EPSILON);
918 #endif
919
920 default:
921 {
922 char msg[100];
923 sprintf(msg, "Unknown real arithmetic function: %d", c);
924 raise_exception_string(mdTaskData, EXC_Fail, msg);
925 return 0;
926 }
927 }
928 }
929
PolyRealSize(PolyWord)930 POLYUNSIGNED PolyRealSize(PolyWord)
931 {
932 // Return the number of bytes for a real. This is used in PackRealBig/Little.
933 return TAGGED(sizeof(double)).AsUnsigned();
934 }
935
PolyRealGeneral(FirstArgument threadId,PolyWord code,PolyWord arg)936 POLYUNSIGNED PolyRealGeneral(FirstArgument threadId, PolyWord code, PolyWord arg)
937 {
938 TaskData *taskData = TaskData::FindTaskForId(threadId);
939 ASSERT(taskData != 0);
940 taskData->PreRTSCall();
941 Handle reset = taskData->saveVec.mark();
942 Handle pushedCode = taskData->saveVec.push(code);
943 Handle pushedArg = taskData->saveVec.push(arg);
944 Handle result = 0;
945
946 try {
947 result = Real_dispatchc(taskData, pushedArg, pushedCode);
948 } catch (...) { } // If an ML exception is raised
949
950 taskData->saveVec.reset(reset);
951 taskData->PostRTSCall();
952 if (result == 0) return TAGGED(0).AsUnsigned();
953 else return result->Word().AsUnsigned();
954 }
955
956 struct _entrypts realsEPT[] =
957 {
958 { "PolyRealBoxedToString", (polyRTSFunction)&PolyRealBoxedToString},
959 { "PolyRealGeneral", (polyRTSFunction)&PolyRealGeneral},
960 { "PolyRealBoxedFromString", (polyRTSFunction)&PolyRealBoxedFromString},
961 { "PolyRealBoxedToLongInt", (polyRTSFunction)&PolyRealBoxedToLongInt},
962 { "PolyRealSqrt", (polyRTSFunction)&PolyRealSqrt},
963 { "PolyRealSin", (polyRTSFunction)&PolyRealSin},
964 { "PolyRealCos", (polyRTSFunction)&PolyRealCos},
965 { "PolyRealArctan", (polyRTSFunction)&PolyRealArctan},
966 { "PolyRealExp", (polyRTSFunction)&PolyRealExp},
967 { "PolyRealLog", (polyRTSFunction)&PolyRealLog},
968 { "PolyRealTan", (polyRTSFunction)&PolyRealTan},
969 { "PolyRealArcSin", (polyRTSFunction)&PolyRealArcSin},
970 { "PolyRealArcCos", (polyRTSFunction)&PolyRealArcCos},
971 { "PolyRealLog10", (polyRTSFunction)&PolyRealLog10},
972 { "PolyRealSinh", (polyRTSFunction)&PolyRealSinh},
973 { "PolyRealCosh", (polyRTSFunction)&PolyRealCosh},
974 { "PolyRealTanh", (polyRTSFunction)&PolyRealTanh},
975 { "PolyRealFloor", (polyRTSFunction)&PolyRealFloor},
976 { "PolyRealCeil", (polyRTSFunction)&PolyRealCeil},
977 { "PolyRealTrunc", (polyRTSFunction)&PolyRealTrunc},
978 { "PolyRealRound", (polyRTSFunction)&PolyRealRound},
979 { "PolyRealRem", (polyRTSFunction)&PolyRealRem },
980 { "PolyFloatArbitraryPrecision", (polyRTSFunction)&PolyFloatArbitraryPrecision},
981 { "PolyGetRoundingMode", (polyRTSFunction)&PolyGetRoundingMode},
982 { "PolySetRoundingMode", (polyRTSFunction)&PolySetRoundingMode},
983 { "PolyRealSize", (polyRTSFunction)&PolyRealSize},
984 { "PolyRealAtan2", (polyRTSFunction)&PolyRealAtan2 },
985 { "PolyRealPow", (polyRTSFunction)&PolyRealPow },
986 { "PolyRealCopySign", (polyRTSFunction)&PolyRealCopySign },
987 { "PolyRealNextAfter", (polyRTSFunction)&PolyRealNextAfter },
988 { "PolyRealLdexp", (polyRTSFunction)&PolyRealLdexp },
989 { "PolyRealFrexp", (polyRTSFunction)&PolyRealFrexp },
990 { "PolyRealFSqrt", (polyRTSFunction)&PolyRealFSqrt },
991 { "PolyRealFSin", (polyRTSFunction)&PolyRealFSin },
992 { "PolyRealFCos", (polyRTSFunction)&PolyRealFCos },
993 { "PolyRealFArctan", (polyRTSFunction)&PolyRealFArctan },
994 { "PolyRealFExp", (polyRTSFunction)&PolyRealFExp },
995 { "PolyRealFLog", (polyRTSFunction)&PolyRealFLog },
996 { "PolyRealFTan", (polyRTSFunction)&PolyRealFTan },
997 { "PolyRealFArcSin", (polyRTSFunction)&PolyRealFArcSin },
998 { "PolyRealFArcCos", (polyRTSFunction)&PolyRealFArcCos },
999 { "PolyRealFLog10", (polyRTSFunction)&PolyRealFLog10 },
1000 { "PolyRealFSinh", (polyRTSFunction)&PolyRealFSinh },
1001 { "PolyRealFCosh", (polyRTSFunction)&PolyRealFCosh },
1002 { "PolyRealFTanh", (polyRTSFunction)&PolyRealFTanh },
1003 { "PolyRealFAtan2", (polyRTSFunction)&PolyRealFAtan2 },
1004 { "PolyRealFPow", (polyRTSFunction)&PolyRealFPow },
1005 { "PolyRealFCopySign", (polyRTSFunction)&PolyRealFCopySign },
1006 { "PolyRealFFloor", (polyRTSFunction)&PolyRealFFloor },
1007 { "PolyRealFCeil", (polyRTSFunction)&PolyRealFCeil },
1008 { "PolyRealFTrunc", (polyRTSFunction)&PolyRealFTrunc },
1009 { "PolyRealFRound", (polyRTSFunction)&PolyRealFRound },
1010 { "PolyRealFRem", (polyRTSFunction)&PolyRealFRem },
1011 { "PolyRealFNextAfter", (polyRTSFunction)&PolyRealFNextAfter },
1012
1013 { NULL, NULL} // End of list.
1014 };
1015
1016 class RealArithmetic: public RtsModule
1017 {
1018 public:
1019 virtual void Init(void);
1020 };
1021
1022 // Declare this. It will be automatically added to the table.
1023 static RealArithmetic realModule;
1024
Init(void)1025 void RealArithmetic::Init(void)
1026 {
1027 /* Some compilers object to overflow in constants so
1028 we compute the values here. */
1029 #if (HAVE_DECL_FPSETMASK && ! defined(__CYGWIN__))
1030 /* In FreeBSD 3.4 at least, we sometimes get floating point
1031 exceptions if we don't clear the mask. Maybe need to do
1032 this on other platforms as well just to be sure. */
1033 // N.B. fpsetmask is defined in the headers on Cygwin but there's no function!
1034 fpsetmask(0);
1035 #endif
1036 // NAN and INFINITY are defined in GCC but not in Visual C++.
1037 #if (defined(INFINITY))
1038 posInf = INFINITY;
1039 negInf = -(INFINITY);
1040 posInfF = INFINITY;
1041 negInfF = -(INFINITY);
1042 #else
1043 {
1044 double zero = 0.0;
1045 posInf = 1.0 / zero;
1046 negInf = -1.0 / zero;
1047 float zeroF = 0.0;
1048 posInfF = 1.0 / zeroF;
1049 negInfF = -1.0 / zeroF;
1050 }
1051 #endif
1052 #if (defined(NAN))
1053 notANumber = NAN;
1054 #else
1055 {
1056 double zero = 0.0;
1057 notANumber = zero / zero;
1058 float zeroF = 0.0;
1059 notANumberF = zeroF / zeroF;
1060 }
1061 #endif
1062 // Make sure this is a positive NaN since we return it from "abs".
1063 // "Positive" in this context is copysign(1.0, x) > 0.0 because that's
1064 // how we test the sign so we test it first and then try to change the
1065 // sign if it's wrong.
1066 if (copysign(1.0, notANumber) < 0)
1067 notANumber = copysign(notANumber, 1.0);
1068 if (copysignf(1.0, notANumberF) < 0)
1069 notANumberF = copysignf(notANumberF, 1.0);
1070 }
1071