397 lines
8.5 KiB
C
397 lines
8.5 KiB
C
/***********************************************************************/
|
|
/* */
|
|
/* Objective Caml */
|
|
/* */
|
|
/* Xavier Leroy, projet Cristal, INRIA Rocquencourt */
|
|
/* */
|
|
/* Copyright 1996 Institut National de Recherche en Informatique et */
|
|
/* en Automatique. All rights reserved. This file is distributed */
|
|
/* under the terms of the GNU Library General Public License, with */
|
|
/* the special exception on linking described in file ../LICENSE. */
|
|
/* */
|
|
/***********************************************************************/
|
|
|
|
/* $Id$ */
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "alloc.h"
|
|
#include "fail.h"
|
|
#include "memory.h"
|
|
#include "mlvalues.h"
|
|
#include "misc.h"
|
|
#include "reverse.h"
|
|
#include "stacks.h"
|
|
|
|
#ifdef ARCH_ALIGN_DOUBLE
|
|
|
|
CAMLexport double Double_val(value val)
|
|
{
|
|
union { value v[2]; double d; } buffer;
|
|
|
|
Assert(sizeof(double) == 2 * sizeof(value));
|
|
buffer.v[0] = Field(val, 0);
|
|
buffer.v[1] = Field(val, 1);
|
|
return buffer.d;
|
|
}
|
|
|
|
CAMLexport void Store_double_val(value val, double dbl)
|
|
{
|
|
union { value v[2]; double d; } buffer;
|
|
|
|
Assert(sizeof(double) == 2 * sizeof(value));
|
|
buffer.d = dbl;
|
|
Field(val, 0) = buffer.v[0];
|
|
Field(val, 1) = buffer.v[1];
|
|
}
|
|
|
|
#endif
|
|
|
|
CAMLexport value copy_double(double d)
|
|
{
|
|
value res;
|
|
|
|
#define Setup_for_gc
|
|
#define Restore_after_gc
|
|
Alloc_small(res, Double_wosize, Double_tag);
|
|
#undef Setup_for_gc
|
|
#undef Restore_after_gc
|
|
Store_double_val(res, d);
|
|
return res;
|
|
}
|
|
|
|
CAMLprim value format_float(value fmt, value arg)
|
|
{
|
|
#define MAX_DIGITS 350
|
|
/* Max number of decimal digits in a "natural" (not artificially padded)
|
|
representation of a float. Can be quite big for %f format.
|
|
Max exponent for IEEE format is 308 decimal digits.
|
|
Rounded up for good measure. */
|
|
char format_buffer[MAX_DIGITS + 20];
|
|
int prec, i;
|
|
char * p;
|
|
char * dest;
|
|
value res;
|
|
|
|
prec = MAX_DIGITS;
|
|
for (p = String_val(fmt); *p != 0; p++) {
|
|
if (*p >= '0' && *p <= '9') {
|
|
i = atoi(p) + MAX_DIGITS;
|
|
if (i > prec) prec = i;
|
|
break;
|
|
}
|
|
}
|
|
for( ; *p != 0; p++) {
|
|
if (*p == '.') {
|
|
i = atoi(p+1) + MAX_DIGITS;
|
|
if (i > prec) prec = i;
|
|
break;
|
|
}
|
|
}
|
|
if (prec < sizeof(format_buffer)) {
|
|
dest = format_buffer;
|
|
} else {
|
|
dest = stat_alloc(prec);
|
|
}
|
|
sprintf(dest, String_val(fmt), Double_val(arg));
|
|
res = copy_string(dest);
|
|
if (dest != format_buffer) {
|
|
stat_free(dest);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
CAMLprim value float_of_string(value vs)
|
|
{
|
|
char parse_buffer[64];
|
|
char * buf, * src, * dst, * end;
|
|
mlsize_t len;
|
|
double d;
|
|
|
|
len = string_length(vs);
|
|
buf = len < sizeof(parse_buffer) ? parse_buffer : stat_alloc(len + 1);
|
|
src = String_val(vs);
|
|
dst = buf;
|
|
while (len--) {
|
|
char c = *src++;
|
|
if (c != '_') *dst++ = c;
|
|
}
|
|
*dst = 0;
|
|
d = strtod((const char *) buf, &end);
|
|
if (buf != parse_buffer) stat_free(buf);
|
|
if (end != dst) failwith("float_of_string");
|
|
return copy_double(d);
|
|
}
|
|
|
|
CAMLprim value int_of_float(value f)
|
|
{
|
|
return Val_long((long) Double_val(f));
|
|
}
|
|
|
|
CAMLprim value float_of_int(value n)
|
|
{
|
|
return copy_double((double) Long_val(n));
|
|
}
|
|
|
|
CAMLprim value neg_float(value f)
|
|
{
|
|
return copy_double(- Double_val(f));
|
|
}
|
|
|
|
CAMLprim value abs_float(value f)
|
|
{
|
|
return copy_double(fabs(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value add_float(value f, value g)
|
|
{
|
|
return copy_double(Double_val(f) + Double_val(g));
|
|
}
|
|
|
|
CAMLprim value sub_float(value f, value g)
|
|
{
|
|
return copy_double(Double_val(f) - Double_val(g));
|
|
}
|
|
|
|
CAMLprim value mul_float(value f, value g)
|
|
{
|
|
return copy_double(Double_val(f) * Double_val(g));
|
|
}
|
|
|
|
CAMLprim value div_float(value f, value g)
|
|
{
|
|
return copy_double(Double_val(f) / Double_val(g));
|
|
}
|
|
|
|
CAMLprim value exp_float(value f)
|
|
{
|
|
return copy_double(exp(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value floor_float(value f)
|
|
{
|
|
return copy_double(floor(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value fmod_float(value f1, value f2)
|
|
{
|
|
return copy_double(fmod(Double_val(f1), Double_val(f2)));
|
|
}
|
|
|
|
CAMLprim value frexp_float(value f)
|
|
{
|
|
CAMLparam1 (f);
|
|
CAMLlocal2 (res, mantissa);
|
|
int exponent;
|
|
|
|
mantissa = copy_double(frexp (Double_val(f), &exponent));
|
|
res = alloc_tuple(2);
|
|
Field(res, 0) = mantissa;
|
|
Field(res, 1) = Val_int(exponent);
|
|
CAMLreturn (res);
|
|
}
|
|
|
|
CAMLprim value ldexp_float(value f, value i)
|
|
{
|
|
return copy_double(ldexp(Double_val(f), Int_val(i)));
|
|
}
|
|
|
|
CAMLprim value log_float(value f)
|
|
{
|
|
return copy_double(log(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value log10_float(value f)
|
|
{
|
|
return copy_double(log10(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value modf_float(value f)
|
|
{
|
|
#if __SC__
|
|
_float_eval frem; /* Problem with Apple's <math.h> */
|
|
#else
|
|
double frem;
|
|
#endif
|
|
CAMLparam1 (f);
|
|
CAMLlocal3 (res, quo, rem);
|
|
|
|
quo = copy_double(modf (Double_val(f), &frem));
|
|
rem = copy_double(frem);
|
|
res = alloc_tuple(2);
|
|
Field(res, 0) = quo;
|
|
Field(res, 1) = rem;
|
|
CAMLreturn (res);
|
|
}
|
|
|
|
CAMLprim value sqrt_float(value f)
|
|
{
|
|
return copy_double(sqrt(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value power_float(value f, value g)
|
|
{
|
|
return copy_double(pow(Double_val(f), Double_val(g)));
|
|
}
|
|
|
|
CAMLprim value sin_float(value f)
|
|
{
|
|
return copy_double(sin(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value sinh_float(value f)
|
|
{
|
|
return copy_double(sinh(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value cos_float(value f)
|
|
{
|
|
return copy_double(cos(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value cosh_float(value f)
|
|
{
|
|
return copy_double(cosh(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value tan_float(value f)
|
|
{
|
|
return copy_double(tan(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value tanh_float(value f)
|
|
{
|
|
return copy_double(tanh(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value asin_float(value f)
|
|
{
|
|
return copy_double(asin(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value acos_float(value f)
|
|
{
|
|
return copy_double(acos(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value atan_float(value f)
|
|
{
|
|
return copy_double(atan(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value atan2_float(value f, value g)
|
|
{
|
|
return copy_double(atan2(Double_val(f), Double_val(g)));
|
|
}
|
|
|
|
CAMLprim value ceil_float(value f)
|
|
{
|
|
return copy_double(ceil(Double_val(f)));
|
|
}
|
|
|
|
CAMLprim value eq_float(value f, value g)
|
|
{
|
|
return Val_bool(Double_val(f) == Double_val(g));
|
|
}
|
|
|
|
CAMLprim value neq_float(value f, value g)
|
|
{
|
|
return Val_bool(Double_val(f) != Double_val(g));
|
|
}
|
|
|
|
CAMLprim value le_float(value f, value g)
|
|
{
|
|
return Val_bool(Double_val(f) <= Double_val(g));
|
|
}
|
|
|
|
CAMLprim value lt_float(value f, value g)
|
|
{
|
|
return Val_bool(Double_val(f) < Double_val(g));
|
|
}
|
|
|
|
CAMLprim value ge_float(value f, value g)
|
|
{
|
|
return Val_bool(Double_val(f) >= Double_val(g));
|
|
}
|
|
|
|
CAMLprim value gt_float(value f, value g)
|
|
{
|
|
return Val_bool(Double_val(f) > Double_val(g));
|
|
}
|
|
|
|
CAMLprim value float_of_bytes(value s)
|
|
{
|
|
value d = copy_double(0.0);
|
|
#ifdef ARCH_BIG_ENDIAN
|
|
memcpy(String_val(d), String_val(s), 8);
|
|
#else
|
|
Reverse_64(String_val(d), String_val(s));
|
|
#endif
|
|
return d;
|
|
}
|
|
|
|
enum { FP_normal, FP_subnormal, FP_zero, FP_infinite, FP_nan };
|
|
|
|
CAMLprim value classify_float(value vd)
|
|
{
|
|
/* Cygwin 1.3 has problems with fpclassify (PR#1293), so don't use it */
|
|
#if defined(fpclassify) && !defined(__CYGWIN32__) && !defined(__MINGW32__)
|
|
switch (fpclassify(Double_val(vd))) {
|
|
case FP_NAN:
|
|
return Val_int(FP_nan);
|
|
case FP_INFINITE:
|
|
return Val_int(FP_infinite);
|
|
case FP_ZERO:
|
|
return Val_int(FP_zero);
|
|
case FP_SUBNORMAL:
|
|
return Val_int(FP_subnormal);
|
|
default: /* case FP_NORMAL */
|
|
return Val_int(FP_normal);
|
|
}
|
|
#else
|
|
double d = Double_val(vd);
|
|
uint32 h, l;
|
|
#ifdef ARCH_BIG_ENDIAN
|
|
h = ((uint32 *) &d)[0];
|
|
l = ((uint32 *) &d)[1];
|
|
#else
|
|
l = ((uint32 *) &d)[0];
|
|
h = ((uint32 *) &d)[1];
|
|
#endif
|
|
l = l | (h & 0xFFFFF);
|
|
h = h & 0x7FF00000;
|
|
if ((h | l) == 0)
|
|
return Val_int(FP_zero);
|
|
if (h == 0)
|
|
return Val_int(FP_subnormal);
|
|
if (h == 0x7FF00000) {
|
|
if (l == 0)
|
|
return Val_int(FP_infinite);
|
|
else
|
|
return Val_int(FP_nan);
|
|
}
|
|
return Val_int(FP_normal);
|
|
#endif
|
|
}
|
|
|
|
/* The init_ieee_float function should initialize floating-point hardware
|
|
so that it behaves as much as possible like the IEEE standard.
|
|
In particular, return special numbers like Infinity and NaN instead
|
|
of signalling exceptions. Currently, everyone is in IEEE mode
|
|
at program startup, except FreeBSD prior to 4.0R. */
|
|
|
|
#ifdef __FreeBSD__
|
|
#include <osreldate.h>
|
|
#if (__FreeBSD_version < 400017)
|
|
#include <floatingpoint.h>
|
|
#endif
|
|
#endif
|
|
|
|
void init_ieee_floats(void)
|
|
{
|
|
#if defined(__FreeBSD__) && (__FreeBSD_version < 400017)
|
|
fpsetmask(0);
|
|
#endif
|
|
}
|