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