mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			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;
 | 
						|
}
 |