1 /* VMDouble.c - java.lang.VMDouble native functions
2    Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005, 2006, 2007
3    Free Software Foundation, Inc.
4 
5 This file is part of GNU Classpath.
6 
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11 
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21 
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26 
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38 
39 
40 #include <assert.h>
41 #include <config.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 
46 #include "mprec.h"
47 #include "fdlibm.h"
48 #include "jcl.h"
49 
50 #include "java_lang_VMDouble.h"
51 
52 static jclass clsDouble;
53 static jmethodID isNaNID;
54 static jdouble NEGATIVE_INFINITY;
55 static jdouble POSITIVE_INFINITY;
56 static jdouble NaN;
57 
58 /*
59  * Class:     java_lang_VMDouble
60  * Method:    initIDs
61  * Signature: ()V
62  */
63 JNIEXPORT void JNICALL
Java_java_lang_VMDouble_initIDs(JNIEnv * env,jclass cls)64 Java_java_lang_VMDouble_initIDs (JNIEnv * env, jclass cls __attribute__ ((__unused__)))
65 {
66   jfieldID negInfID;
67   jfieldID posInfID;
68   jfieldID nanID;
69 
70   clsDouble = (*env)->FindClass (env, "java/lang/Double");
71   if (clsDouble == NULL)
72     {
73       DBG ("unable to get class java.lang.Double\n") return;
74     }
75   clsDouble = (*env)->NewGlobalRef(env, clsDouble);
76   if (clsDouble == NULL)
77     {
78       DBG ("unable to register class java.lang.Double as global ref\n") return;
79     }
80   isNaNID = (*env)->GetStaticMethodID (env, clsDouble, "isNaN", "(D)Z");
81   if (isNaNID == NULL)
82     {
83       DBG ("unable to determine method id of isNaN\n") return;
84     }
85   negInfID = (*env)->GetStaticFieldID (env, clsDouble, "NEGATIVE_INFINITY", "D");
86   if (negInfID == NULL)
87     {
88       DBG ("unable to determine field id of NEGATIVE_INFINITY\n") return;
89     }
90   posInfID = (*env)->GetStaticFieldID (env, clsDouble, "POSITIVE_INFINITY", "D");
91   if (posInfID == NULL)
92     {
93       DBG ("unable to determine field id of POSITIVE_INFINITY\n") return;
94     }
95   nanID = (*env)->GetStaticFieldID (env, clsDouble, "NaN", "D");
96   if (posInfID == NULL)
97     {
98       DBG ("unable to determine field id of NaN\n") return;
99     }
100   POSITIVE_INFINITY = (*env)->GetStaticDoubleField (env, clsDouble, posInfID);
101   NEGATIVE_INFINITY = (*env)->GetStaticDoubleField (env, clsDouble, negInfID);
102   NaN = (*env)->GetStaticDoubleField (env, clsDouble, nanID);
103 
104 #ifdef DEBUG
105   fprintf (stderr, "java.lang.Double.initIDs() POSITIVE_INFINITY = %g\n",
106 	   POSITIVE_INFINITY);
107   fprintf (stderr, "java.lang.Double.initIDs() NEGATIVE_INFINITY = %g\n",
108 	   NEGATIVE_INFINITY);
109   fprintf (stderr, "java.lang.Double.initIDs() NaN = %g\n", NaN);
110 #endif
111 }
112 
113 /*
114  * Class:     java_lang_VMDouble
115  * Method:    doubleToRawLongBits
116  * Signature: (D)J
117  */
118 JNIEXPORT jlong JNICALL
Java_java_lang_VMDouble_doubleToRawLongBits(JNIEnv * env,jclass cls,jdouble doubleValue)119 Java_java_lang_VMDouble_doubleToRawLongBits
120   (JNIEnv * env __attribute__ ((__unused__)),
121    jclass cls __attribute__ ((__unused__)), jdouble doubleValue)
122 {
123   jvalue val;
124 
125   val.d = doubleValue;
126 
127 #if defined(__IEEE_BYTES_LITTLE_ENDIAN)
128   /* On little endian ARM processors when using FPA, word order of
129      doubles is still big endian. So take that into account here. When
130      using VFP, word order of doubles follows byte order. */
131 
132 #define SWAP_DOUBLE(a)    (((a) << 32) | (((a) >> 32) & 0x00000000ffffffff))
133 
134   val.j = SWAP_DOUBLE(val.j);
135 #endif
136 
137   return val.j;
138 }
139 
140 /*
141  * Class:     java_lang_VMDouble
142  * Method:    longBitsToDouble
143  * Signature: (J)D
144  */
145 JNIEXPORT jdouble JNICALL
Java_java_lang_VMDouble_longBitsToDouble(JNIEnv * env,jclass cls,jlong longValue)146 Java_java_lang_VMDouble_longBitsToDouble
147   (JNIEnv * env __attribute__ ((__unused__)),
148    jclass cls __attribute__ ((__unused__)), jlong longValue)
149 {
150   jvalue val;
151 
152   val.j = longValue;
153 
154 #if defined(__IEEE_BYTES_LITTLE_ENDIAN)
155   val.j = SWAP_DOUBLE(val.j);
156 #endif
157 
158   return val.d;
159 }
160 
161 /**
162  * Parse a double from a char array.
163  */
164 static jdouble
parseDoubleFromChars(JNIEnv * env,const char * buf)165 parseDoubleFromChars(JNIEnv * env, const char * buf)
166 {
167   char *endptr;
168   jdouble val = 0.0;
169   const char *p = buf, *end, *last_non_ws, *temp;
170   int ok = 1;
171 
172 #ifdef DEBUG
173   fprintf (stderr, "java.lang.VMDouble.parseDouble (%s)\n", buf);
174 #endif
175 
176   /* Trim the buffer, similar to String.trim().  First the leading
177      characters.  */
178   while (*p && *p <= ' ')
179     ++p;
180 
181   /* Find the last non-whitespace character.  This method is safe
182      even with multi-byte UTF-8 characters.  */
183   end = p;
184   last_non_ws = NULL;
185   while (*end)
186     {
187       if (*end > ' ')
188 	last_non_ws = end;
189       ++end;
190     }
191 
192   if (last_non_ws == NULL)
193     last_non_ws = p + strlen (p);
194   else
195     {
196       /* Skip past the last non-whitespace character.  */
197       ++last_non_ws;
198     }
199 
200   /* Check for infinity and NaN */
201   temp = p;
202   if (temp[0] == '+' || temp[0] == '-')
203     temp++;
204   if (strncmp ("Infinity", temp, (size_t) 8) == 0)
205     {
206       if (p[0] == '-')
207 	return NEGATIVE_INFINITY;
208       return POSITIVE_INFINITY;
209     }
210   if (strncmp ("NaN", temp, (size_t) 3) == 0)
211     return NaN;
212 
213   /* Skip a trailing `f' or `d'.  */
214   if (last_non_ws > p
215       && (last_non_ws[-1] == 'f'
216 	  || last_non_ws[-1] == 'F'
217 	  || last_non_ws[-1] == 'd' || last_non_ws[-1] == 'D'))
218     --last_non_ws;
219 
220   if (last_non_ws > p)
221     {
222       struct _Jv_reent reent;
223       memset (&reent, 0, sizeof reent);
224 
225       val = _strtod_r (&reent, p, &endptr);
226 
227 #ifdef DEBUG
228       fprintf (stderr, "java.lang.VMDouble.parseDouble val = %g\n", val);
229       fprintf (stderr, "java.lang.VMDouble.parseDouble %p != %p ???\n",
230 	       endptr, last_non_ws);
231 #endif
232       if (endptr != last_non_ws)
233 	ok = 0;
234     }
235   else
236     ok = 0;
237 
238   if (!ok)
239     {
240       val = 0.0;
241       JCL_ThrowException (env,
242 			  "java/lang/NumberFormatException",
243 			  "unable to parse double");
244     }
245 
246   return val;
247 }
248 
249 #define MAXIMAL_DECIMAL_STRING_LENGTH 64
250 
251 /**
252  * Use _dtoa to print a double or a float as a string with the given precision.
253  */
254 static void
dtoa_toString(char * buffer,jdouble value,jint precision,jboolean isFloat)255 dtoa_toString
256 (char * buffer, jdouble value, jint precision, jboolean isFloat)
257 {
258   const int DTOA_MODE = 2;
259   char result[MAXIMAL_DECIMAL_STRING_LENGTH];
260   int decpt, sign;
261   char *s, *d;
262   int i;
263 
264   /* use mode 2 to get at the digit stream, all other modes are useless
265    *
266    * since mode 2 only gives us as many digits as we need in precision, we need to
267    * add the digits in front of the floating point to it, if there is more than one
268    * to be printed. That's the case if the value is going to be printed using the
269    * normal notation, i.e. if it is 0 or >= 1.0e-3 and < 1.0e7.
270    */
271   int digits_in_front_of_floating_point = ceil(log10(value));
272 
273   if (digits_in_front_of_floating_point > 1 && digits_in_front_of_floating_point < 7)
274     precision += digits_in_front_of_floating_point;
275 
276   _dtoa (value, DTOA_MODE, precision, &decpt, &sign, NULL, buffer, (int) isFloat);
277 
278   value = fabs (value);
279 
280   s = buffer;
281   d = result;
282 
283   /* Handle negative sign */
284   if (sign)
285     *d++ = '-';
286 
287   /* Handle normal represenation */
288   if ((value >= 1e-3 && value < 1e7) || (value == 0))
289     {
290       if (decpt <= 0)
291 	*d++ = '0';
292       else
293 	{
294 	  for (i = 0; i < decpt; i++)
295 	    if (*s)
296 	      *d++ = *s++;
297 	    else
298 	      *d++ = '0';
299 	}
300 
301       *d++ = '.';
302 
303       if (*s == 0)
304 	{
305 	  *d++ = '0';
306 	  decpt++;
307 	}
308 
309       while (decpt++ < 0)
310 	*d++ = '0';
311 
312       while (*s)
313 	*d++ = *s++;
314 
315       *d = 0;
316 
317     }
318   /* Handle scientific representaiton */
319   else
320     {
321       *d++ = *s++;
322       decpt--;
323       *d++ = '.';
324 
325       if (*s == 0)
326 	*d++ = '0';
327 
328       while (*s)
329 	*d++ = *s++;
330 
331       *d++ = 'E';
332 
333       if (decpt < 0)
334 	{
335 	  *d++ = '-';
336 	  decpt = -decpt;
337 	}
338 
339       {
340 	char exp[4];
341 	char *e = exp + sizeof exp;
342 
343 	*--e = 0;
344 	do
345 	  {
346 	    *--e = '0' + decpt % 10;
347 	    decpt /= 10;
348 	  }
349 	while (decpt > 0);
350 
351 	while (*e)
352 	  *d++ = *e++;
353       }
354 
355       *d = 0;
356     }
357 
358   /* copy the result into the buffer */
359   memcpy(buffer, result, MAXIMAL_DECIMAL_STRING_LENGTH);
360 }
361 
362 /*
363  * Class:     java_lang_VMDouble
364  * Method:    toString
365  * Signature: (DZ)Ljava/lang/String;
366  */
367 JNIEXPORT jstring JNICALL
Java_java_lang_VMDouble_toString(JNIEnv * env,jclass cls,jdouble value,jboolean isFloat)368 Java_java_lang_VMDouble_toString
369   (JNIEnv * env, jclass cls __attribute__ ((__unused__)), jdouble value, jboolean isFloat)
370 {
371   char buf[MAXIMAL_DECIMAL_STRING_LENGTH];
372   const jint MAXIMAL_FLOAT_PRECISION = 10;
373   const jint MAXIMAL_DOUBLE_PRECISION = 19;
374 
375   jint maximal_precision;
376   jint least_necessary_precision = 2;
377   jboolean parsed_value_unequal;
378 
379   if ((*env)->CallStaticBooleanMethod (env, clsDouble, isNaNID, value))
380     return (*env)->NewStringUTF (env, "NaN");
381 
382   if (value == POSITIVE_INFINITY)
383     return (*env)->NewStringUTF (env, "Infinity");
384 
385   if (value == NEGATIVE_INFINITY)
386     return (*env)->NewStringUTF (env, "-Infinity");
387 
388   if (isFloat)
389     maximal_precision = MAXIMAL_FLOAT_PRECISION;
390   else
391     maximal_precision = MAXIMAL_DOUBLE_PRECISION;
392 
393   /* Try to find the 'good enough' precision,
394    * that results in enough digits being printed to be able to
395    * convert the number back into the original double, but no
396    * further digits.
397    */
398 
399   do {
400     jdouble parsed_value;
401 
402     assert(least_necessary_precision <= maximal_precision);
403 
404     /* Convert the value to a string and back. */
405     dtoa_toString(buf, value, least_necessary_precision, isFloat);
406 
407     parsed_value = parseDoubleFromChars(env, buf);
408 
409     /* Check whether the original value, and the value after conversion match. */
410     /* We need to cast floats to float to make sure that our ineqality check works
411      * well for floats as well as for doubles.
412      */
413     parsed_value_unequal = ( isFloat ?
414 			     (float) parsed_value != (float) value :
415 			     parsed_value != value);
416 
417     least_necessary_precision++;
418   }
419   while (parsed_value_unequal);
420 
421   return (*env)->NewStringUTF (env, buf);
422 }
423 
424 /*
425  * Class:     java_lang_VMDouble
426  * Method:    parseDouble
427  * Signature: (Ljava/lang/String;)D
428  */
429 JNIEXPORT jdouble JNICALL
Java_java_lang_VMDouble_parseDouble(JNIEnv * env,jclass cls,jstring str)430 Java_java_lang_VMDouble_parseDouble
431   (JNIEnv * env, jclass cls __attribute__ ((__unused__)), jstring str)
432 {
433   jboolean isCopy;
434   const char *buf;
435   jdouble val = 0.0;
436 
437   if (str == NULL)
438     {
439       JCL_ThrowException (env, "java/lang/NullPointerException", "null");
440       return val;
441     }
442 
443   buf = (*env)->GetStringUTFChars (env, str, &isCopy);
444   if (buf == NULL)
445     {
446       /* OutOfMemoryError already thrown */
447     }
448   else
449     {
450       val = parseDoubleFromChars(env, buf);
451       (*env)->ReleaseStringUTFChars (env, str, buf);
452     }
453 
454   return val;
455 }
456