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