summaryrefslogtreecommitdiff
path: root/native/jni
diff options
context:
space:
mode:
authorDalibor Topic <robilad@yahoo.com>2006-11-28 22:00:23 +0000
committerDalibor Topic <robilad@yahoo.com>2006-11-28 22:00:23 +0000
commit9dca1e22bf86129a8adc4e021ee7c760b9f1773b (patch)
tree2b6e8bc7a2961b2b8f66ed9fee68a6daaa17b08e /native/jni
parentf653b9742c6d2de88d5eb1ffbe439f51a7a82fae (diff)
downloadclasspath-9dca1e22bf86129a8adc4e021ee7c760b9f1773b.tar.gz
Fix for printing Double.MIN_VALUE and Float.MIN_VALUEgenerics-merge-20061128
2006-11-28 Dalibor Topic <robilad@kaffe.org> * native/jni/java-lang/java_lang_VMDouble.c: (parseDoubleFromChars) New function. Factored out from ... (Java_java_lang_VMDouble_parseDouble): Factored out the parsing. (dtoa_toString): New function. Factored out from ... (Java_java_lang_VMDouble_toString) : Factored out the conversion. Changed conversion mode to 2, as modes 0 and 1 don't round as the API spec demands. Invoke conversion function as often as necessary with growing precision until a reversible representation of the double in form of a string is reached.
Diffstat (limited to 'native/jni')
-rw-r--r--native/jni/java-lang/java_lang_VMDouble.c329
1 files changed, 204 insertions, 125 deletions
diff --git a/native/jni/java-lang/java_lang_VMDouble.c b/native/jni/java-lang/java_lang_VMDouble.c
index f8c072192..2ee1f3146 100644
--- a/native/jni/java-lang/java_lang_VMDouble.c
+++ b/native/jni/java-lang/java_lang_VMDouble.c
@@ -37,6 +37,7 @@ 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>
@@ -167,43 +168,133 @@ Java_java_lang_VMDouble_longBitsToDouble
return val.d;
}
-/*
- * Class: java_lang_VMDouble
- * Method: toString
- * Signature: (DZ)Ljava/lang/String;
+/**
+ * Parse a double from a char array.
*/
-JNIEXPORT jstring JNICALL
-Java_java_lang_VMDouble_toString
- (JNIEnv * env, jclass cls __attribute__ ((__unused__)), jdouble value, jboolean isFloat)
+static jdouble
+parseDoubleFromChars(JNIEnv * env, const char * buf)
{
- char buffer[50], result[50];
- int decpt, sign;
- char *s, *d;
- int i;
+ 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.toString (%g)\n", value);
+ fprintf (stderr, "java.lang.VMDouble.parseDouble (%s)\n", buf);
#endif
- if ((*env)->CallStaticBooleanMethod (env, clsDouble, isNaNID, value))
- return (*env)->NewStringUTF (env, "NaN");
+ /* Trim the buffer, similar to String.trim(). First the leading
+ characters. */
+ while (*p && *p <= ' ')
+ ++p;
- if (value == POSITIVE_INFINITY)
- return (*env)->NewStringUTF (env, "Infinity");
+ /* 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 (value == NEGATIVE_INFINITY)
- return (*env)->NewStringUTF (env, "-Infinity");
+ 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);
- _dtoa (value, 0, 20, &decpt, &sign, NULL, buffer, (int) isFloat);
+ val = _strtod_r (&reent, p, &endptr);
+
+#ifdef DEBUG
+ fprintf (stderr, "java.lang.VMDouble.parseDouble val = %g\n", val);
+ fprintf (stderr, "java.lang.VMDouble.parseDouble %i != %i ???\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)
@@ -233,46 +324,111 @@ Java_java_lang_VMDouble_toString
*d = 0;
- return (*env)->NewStringUTF (env, result);
}
+ /* Handle scientific representaiton */
+ else
+ {
+ *d++ = *s++;
+ decpt--;
+ *d++ = '.';
- *d++ = *s++;
- decpt--;
- *d++ = '.';
+ if (*s == 0)
+ *d++ = '0';
- if (*s == 0)
- *d++ = '0';
+ while (*s)
+ *d++ = *s++;
- while (*s)
- *d++ = *s++;
+ *d++ = 'E';
- *d++ = 'E';
+ if (decpt < 0)
+ {
+ *d++ = '-';
+ decpt = -decpt;
+ }
- 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;
}
- {
- char exp[4];
- char *e = exp + sizeof exp;
+ /* copy the result into the buffer */
+ memcpy(buffer, result, MAXIMAL_DECIMAL_STRING_LENGTH);
+}
- *--e = 0;
- do
- {
- *--e = '0' + decpt % 10;
- decpt /= 10;
- }
- while (decpt > 0);
+/*
+ * 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;
- while (*e)
- *d++ = *e++;
- }
+ 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);
- *d = 0;
+ /* 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);
- return (*env)->NewStringUTF (env, result);
+ least_necessary_precision++;
+ }
+ while (parsed_value_unequal);
+
+ return (*env)->NewStringUTF (env, buf);
}
/*
@@ -286,7 +442,6 @@ Java_java_lang_VMDouble_parseDouble
{
jboolean isCopy;
const char *buf;
- char *endptr;
jdouble val = 0.0;
if (str == NULL)
@@ -302,83 +457,7 @@ Java_java_lang_VMDouble_parseDouble
}
else
{
- 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 %i != %i ???\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");
- }
-
+ val = parseDoubleFromChars(env, buf);
(*env)->ReleaseStringUTFChars (env, str, buf);
}