456 lines
11 KiB
C
456 lines
11 KiB
C
/* VMDouble.c - java.lang.VMDouble native functions
|
|
Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005, 2006, 2007
|
|
Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Classpath.
|
|
|
|
GNU Classpath is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
GNU Classpath is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU Classpath; see the file COPYING. If not, write to the
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301 USA.
|
|
|
|
Linking this library statically or dynamically with other modules is
|
|
making a combined work based on this library. Thus, the terms and
|
|
conditions of the GNU General Public License cover the whole
|
|
combination.
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent
|
|
modules, and to copy and distribute the resulting executable under
|
|
terms of your choice, provided that you also meet, for each linked
|
|
independent module, the terms and conditions of the license of that
|
|
module. An independent module is a module which is not derived from
|
|
or based on this library. If you modify this library, you may extend
|
|
this exception to your version of the library, but you are not
|
|
obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version. */
|
|
|
|
|
|
#include <assert.h>
|
|
#include <config.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "mprec.h"
|
|
#include "fdlibm.h"
|
|
#include "jcl.h"
|
|
|
|
#include "java_lang_VMDouble.h"
|
|
|
|
static jclass clsDouble;
|
|
static jmethodID isNaNID;
|
|
static jdouble NEGATIVE_INFINITY;
|
|
static jdouble POSITIVE_INFINITY;
|
|
static jdouble NaN;
|
|
|
|
/*
|
|
* Class: java_lang_VMDouble
|
|
* Method: initIDs
|
|
* Signature: ()V
|
|
*/
|
|
JNIEXPORT void JNICALL
|
|
Java_java_lang_VMDouble_initIDs (JNIEnv * env, jclass cls __attribute__ ((__unused__)))
|
|
{
|
|
jfieldID negInfID;
|
|
jfieldID posInfID;
|
|
jfieldID nanID;
|
|
|
|
clsDouble = (*env)->FindClass (env, "java/lang/Double");
|
|
if (clsDouble == NULL)
|
|
{
|
|
DBG ("unable to get class java.lang.Double\n") return;
|
|
}
|
|
clsDouble = (*env)->NewGlobalRef(env, clsDouble);
|
|
if (clsDouble == NULL)
|
|
{
|
|
DBG ("unable to register class java.lang.Double as global ref\n") return;
|
|
}
|
|
isNaNID = (*env)->GetStaticMethodID (env, clsDouble, "isNaN", "(D)Z");
|
|
if (isNaNID == NULL)
|
|
{
|
|
DBG ("unable to determine method id of isNaN\n") return;
|
|
}
|
|
negInfID = (*env)->GetStaticFieldID (env, clsDouble, "NEGATIVE_INFINITY", "D");
|
|
if (negInfID == NULL)
|
|
{
|
|
DBG ("unable to determine field id of NEGATIVE_INFINITY\n") return;
|
|
}
|
|
posInfID = (*env)->GetStaticFieldID (env, clsDouble, "POSITIVE_INFINITY", "D");
|
|
if (posInfID == NULL)
|
|
{
|
|
DBG ("unable to determine field id of POSITIVE_INFINITY\n") return;
|
|
}
|
|
nanID = (*env)->GetStaticFieldID (env, clsDouble, "NaN", "D");
|
|
if (posInfID == NULL)
|
|
{
|
|
DBG ("unable to determine field id of NaN\n") return;
|
|
}
|
|
POSITIVE_INFINITY = (*env)->GetStaticDoubleField (env, clsDouble, posInfID);
|
|
NEGATIVE_INFINITY = (*env)->GetStaticDoubleField (env, clsDouble, negInfID);
|
|
NaN = (*env)->GetStaticDoubleField (env, clsDouble, nanID);
|
|
|
|
#ifdef DEBUG
|
|
fprintf (stderr, "java.lang.Double.initIDs() POSITIVE_INFINITY = %g\n",
|
|
POSITIVE_INFINITY);
|
|
fprintf (stderr, "java.lang.Double.initIDs() NEGATIVE_INFINITY = %g\n",
|
|
NEGATIVE_INFINITY);
|
|
fprintf (stderr, "java.lang.Double.initIDs() NaN = %g\n", NaN);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Class: java_lang_VMDouble
|
|
* Method: doubleToRawLongBits
|
|
* Signature: (D)J
|
|
*/
|
|
JNIEXPORT jlong JNICALL
|
|
Java_java_lang_VMDouble_doubleToRawLongBits
|
|
(JNIEnv * env __attribute__ ((__unused__)),
|
|
jclass cls __attribute__ ((__unused__)), jdouble doubleValue)
|
|
{
|
|
jvalue val;
|
|
|
|
val.d = doubleValue;
|
|
|
|
#if defined(__IEEE_BYTES_LITTLE_ENDIAN)
|
|
/* On little endian ARM processors when using FPA, word order of
|
|
doubles is still big endian. So take that into account here. When
|
|
using VFP, word order of doubles follows byte order. */
|
|
|
|
#define SWAP_DOUBLE(a) (((a) << 32) | (((a) >> 32) & 0x00000000ffffffff))
|
|
|
|
val.j = SWAP_DOUBLE(val.j);
|
|
#endif
|
|
|
|
return val.j;
|
|
}
|
|
|
|
/*
|
|
* Class: java_lang_VMDouble
|
|
* Method: longBitsToDouble
|
|
* Signature: (J)D
|
|
*/
|
|
JNIEXPORT jdouble JNICALL
|
|
Java_java_lang_VMDouble_longBitsToDouble
|
|
(JNIEnv * env __attribute__ ((__unused__)),
|
|
jclass cls __attribute__ ((__unused__)), jlong longValue)
|
|
{
|
|
jvalue val;
|
|
|
|
val.j = longValue;
|
|
|
|
#if defined(__IEEE_BYTES_LITTLE_ENDIAN)
|
|
val.j = SWAP_DOUBLE(val.j);
|
|
#endif
|
|
|
|
return val.d;
|
|
}
|
|
|
|
/**
|
|
* Parse a double from a char array.
|
|
*/
|
|
static jdouble
|
|
parseDoubleFromChars(JNIEnv * env, const char * buf)
|
|
{
|
|
char *endptr;
|
|
jdouble val = 0.0;
|
|
const char *p = buf, *end, *last_non_ws, *temp;
|
|
int ok = 1;
|
|
|
|
#ifdef DEBUG
|
|
fprintf (stderr, "java.lang.VMDouble.parseDouble (%s)\n", buf);
|
|
#endif
|
|
|
|
/* Trim the buffer, similar to String.trim(). First the leading
|
|
characters. */
|
|
while (*p && *p <= ' ')
|
|
++p;
|
|
|
|
/* Find the last non-whitespace character. This method is safe
|
|
even with multi-byte UTF-8 characters. */
|
|
end = p;
|
|
last_non_ws = NULL;
|
|
while (*end)
|
|
{
|
|
if (*end > ' ')
|
|
last_non_ws = end;
|
|
++end;
|
|
}
|
|
|
|
if (last_non_ws == NULL)
|
|
last_non_ws = p + strlen (p);
|
|
else
|
|
{
|
|
/* Skip past the last non-whitespace character. */
|
|
++last_non_ws;
|
|
}
|
|
|
|
/* Check for infinity and NaN */
|
|
temp = p;
|
|
if (temp[0] == '+' || temp[0] == '-')
|
|
temp++;
|
|
if (strncmp ("Infinity", temp, (size_t) 8) == 0)
|
|
{
|
|
if (p[0] == '-')
|
|
return NEGATIVE_INFINITY;
|
|
return POSITIVE_INFINITY;
|
|
}
|
|
if (strncmp ("NaN", temp, (size_t) 3) == 0)
|
|
return NaN;
|
|
|
|
/* Skip a trailing `f' or `d'. */
|
|
if (last_non_ws > p
|
|
&& (last_non_ws[-1] == 'f'
|
|
|| last_non_ws[-1] == 'F'
|
|
|| last_non_ws[-1] == 'd' || last_non_ws[-1] == 'D'))
|
|
--last_non_ws;
|
|
|
|
if (last_non_ws > p)
|
|
{
|
|
struct _Jv_reent reent;
|
|
memset (&reent, 0, sizeof reent);
|
|
|
|
val = _strtod_r (&reent, p, &endptr);
|
|
|
|
#ifdef DEBUG
|
|
fprintf (stderr, "java.lang.VMDouble.parseDouble val = %g\n", val);
|
|
fprintf (stderr, "java.lang.VMDouble.parseDouble %p != %p ???\n",
|
|
endptr, last_non_ws);
|
|
#endif
|
|
if (endptr != last_non_ws)
|
|
ok = 0;
|
|
}
|
|
else
|
|
ok = 0;
|
|
|
|
if (!ok)
|
|
{
|
|
val = 0.0;
|
|
JCL_ThrowException (env,
|
|
"java/lang/NumberFormatException",
|
|
"unable to parse double");
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
#define MAXIMAL_DECIMAL_STRING_LENGTH 64
|
|
|
|
/**
|
|
* Use _dtoa to print a double or a float as a string with the given precision.
|
|
*/
|
|
static void
|
|
dtoa_toString
|
|
(char * buffer, jdouble value, jint precision, jboolean isFloat)
|
|
{
|
|
const int DTOA_MODE = 2;
|
|
char result[MAXIMAL_DECIMAL_STRING_LENGTH];
|
|
int decpt, sign;
|
|
char *s, *d;
|
|
int i;
|
|
|
|
/* use mode 2 to get at the digit stream, all other modes are useless
|
|
*
|
|
* since mode 2 only gives us as many digits as we need in precision, we need to
|
|
* add the digits in front of the floating point to it, if there is more than one
|
|
* to be printed. That's the case if the value is going to be printed using the
|
|
* normal notation, i.e. if it is 0 or >= 1.0e-3 and < 1.0e7.
|
|
*/
|
|
int digits_in_front_of_floating_point = ceil(log10(value));
|
|
|
|
if (digits_in_front_of_floating_point > 1 && digits_in_front_of_floating_point < 7)
|
|
precision += digits_in_front_of_floating_point;
|
|
|
|
_dtoa (value, DTOA_MODE, precision, &decpt, &sign, NULL, buffer, (int) isFloat);
|
|
|
|
value = fabs (value);
|
|
|
|
s = buffer;
|
|
d = result;
|
|
|
|
/* Handle negative sign */
|
|
if (sign)
|
|
*d++ = '-';
|
|
|
|
/* Handle normal represenation */
|
|
if ((value >= 1e-3 && value < 1e7) || (value == 0))
|
|
{
|
|
if (decpt <= 0)
|
|
*d++ = '0';
|
|
else
|
|
{
|
|
for (i = 0; i < decpt; i++)
|
|
if (*s)
|
|
*d++ = *s++;
|
|
else
|
|
*d++ = '0';
|
|
}
|
|
|
|
*d++ = '.';
|
|
|
|
if (*s == 0)
|
|
{
|
|
*d++ = '0';
|
|
decpt++;
|
|
}
|
|
|
|
while (decpt++ < 0)
|
|
*d++ = '0';
|
|
|
|
while (*s)
|
|
*d++ = *s++;
|
|
|
|
*d = 0;
|
|
|
|
}
|
|
/* Handle scientific representaiton */
|
|
else
|
|
{
|
|
*d++ = *s++;
|
|
decpt--;
|
|
*d++ = '.';
|
|
|
|
if (*s == 0)
|
|
*d++ = '0';
|
|
|
|
while (*s)
|
|
*d++ = *s++;
|
|
|
|
*d++ = 'E';
|
|
|
|
if (decpt < 0)
|
|
{
|
|
*d++ = '-';
|
|
decpt = -decpt;
|
|
}
|
|
|
|
{
|
|
char exp[4];
|
|
char *e = exp + sizeof exp;
|
|
|
|
*--e = 0;
|
|
do
|
|
{
|
|
*--e = '0' + decpt % 10;
|
|
decpt /= 10;
|
|
}
|
|
while (decpt > 0);
|
|
|
|
while (*e)
|
|
*d++ = *e++;
|
|
}
|
|
|
|
*d = 0;
|
|
}
|
|
|
|
/* copy the result into the buffer */
|
|
memcpy(buffer, result, MAXIMAL_DECIMAL_STRING_LENGTH);
|
|
}
|
|
|
|
/*
|
|
* Class: java_lang_VMDouble
|
|
* Method: toString
|
|
* Signature: (DZ)Ljava/lang/String;
|
|
*/
|
|
JNIEXPORT jstring JNICALL
|
|
Java_java_lang_VMDouble_toString
|
|
(JNIEnv * env, jclass cls __attribute__ ((__unused__)), jdouble value, jboolean isFloat)
|
|
{
|
|
char buf[MAXIMAL_DECIMAL_STRING_LENGTH];
|
|
const jint MAXIMAL_FLOAT_PRECISION = 10;
|
|
const jint MAXIMAL_DOUBLE_PRECISION = 19;
|
|
|
|
jint maximal_precision;
|
|
jint least_necessary_precision = 2;
|
|
jboolean parsed_value_unequal;
|
|
|
|
if ((*env)->CallStaticBooleanMethod (env, clsDouble, isNaNID, value))
|
|
return (*env)->NewStringUTF (env, "NaN");
|
|
|
|
if (value == POSITIVE_INFINITY)
|
|
return (*env)->NewStringUTF (env, "Infinity");
|
|
|
|
if (value == NEGATIVE_INFINITY)
|
|
return (*env)->NewStringUTF (env, "-Infinity");
|
|
|
|
if (isFloat)
|
|
maximal_precision = MAXIMAL_FLOAT_PRECISION;
|
|
else
|
|
maximal_precision = MAXIMAL_DOUBLE_PRECISION;
|
|
|
|
/* Try to find the 'good enough' precision,
|
|
* that results in enough digits being printed to be able to
|
|
* convert the number back into the original double, but no
|
|
* further digits.
|
|
*/
|
|
|
|
do {
|
|
jdouble parsed_value;
|
|
|
|
assert(least_necessary_precision <= maximal_precision);
|
|
|
|
/* Convert the value to a string and back. */
|
|
dtoa_toString(buf, value, least_necessary_precision, isFloat);
|
|
|
|
parsed_value = parseDoubleFromChars(env, buf);
|
|
|
|
/* Check whether the original value, and the value after conversion match. */
|
|
/* We need to cast floats to float to make sure that our ineqality check works
|
|
* well for floats as well as for doubles.
|
|
*/
|
|
parsed_value_unequal = ( isFloat ?
|
|
(float) parsed_value != (float) value :
|
|
parsed_value != value);
|
|
|
|
least_necessary_precision++;
|
|
}
|
|
while (parsed_value_unequal);
|
|
|
|
return (*env)->NewStringUTF (env, buf);
|
|
}
|
|
|
|
/*
|
|
* Class: java_lang_VMDouble
|
|
* Method: parseDouble
|
|
* Signature: (Ljava/lang/String;)D
|
|
*/
|
|
JNIEXPORT jdouble JNICALL
|
|
Java_java_lang_VMDouble_parseDouble
|
|
(JNIEnv * env, jclass cls __attribute__ ((__unused__)), jstring str)
|
|
{
|
|
jboolean isCopy;
|
|
const char *buf;
|
|
jdouble val = 0.0;
|
|
|
|
if (str == NULL)
|
|
{
|
|
JCL_ThrowException (env, "java/lang/NullPointerException", "null");
|
|
return val;
|
|
}
|
|
|
|
buf = (*env)->GetStringUTFChars (env, str, &isCopy);
|
|
if (buf == NULL)
|
|
{
|
|
/* OutOfMemoryError already thrown */
|
|
}
|
|
else
|
|
{
|
|
val = parseDoubleFromChars(env, buf);
|
|
(*env)->ReleaseStringUTFChars (env, str, buf);
|
|
}
|
|
|
|
return val;
|
|
}
|