diff options
author | Jingwei Zhang <jingwei.zhang@intel.com> | 2015-01-19 17:13:30 +0800 |
---|---|---|
committer | Elliott Hughes <enh@google.com> | 2015-01-23 12:03:46 -0800 |
commit | 8b24781abd1d49963e5a52a48b85ab5191991ff3 (patch) | |
tree | ebf97445da5e16272553c69fb0655aa39f9ad667 | |
parent | 194940746a618d361b26838e2c6ff04c358adff7 (diff) | |
download | libcore-8b24781abd1d49963e5a52a48b85ab5191991ff3.zip libcore-8b24781abd1d49963e5a52a48b85ab5191991ff3.tar.gz libcore-8b24781abd1d49963e5a52a48b85ab5191991ff3.tar.bz2 |
Fix the infinite loop issue of the conversion from string to double
This patch provide the additional check for “z”.
The “if” statement checks whether the double precision value z, is at the end of a binade
(a term used to describe the set of numbers in a binary IEEE 754 floating-point format that
all have the same exponent, i.e., a binade is the interval [2n, 2n+1) for some value of n.)
If so, it needs to adjust for the change of ulp (unit of least precision is the spacing
between two consecutive floating-point numbers, i.e., the value the least significant digit
represents if it is 1). The adjustment is done by the “simpleShiftLeftHighPrecision” routine.
This is all necessary, except when z is close to denormal (i.e. DOUBLE_TO_LONGBITS(z)==DOUBLE_NORMAL_MASK)
where no adjustment is needed since the ulp should remain the same once z becomes denormal.
This means we can remove the old hack that counted how many times we'd
incremented or decremented, so this patch removes the DECREMENT_DOUBLE
and INCREMENT_DOUBLE macros.
This patch also contains the float equivalent of everything mentioned above,
plus some new tests.
Finally, this patch removes the USE_LL conditional compilation because it
was always true.
Bug: 18087920
Change-Id: I4a9112f012dfd9eeb8db89f0652528b6c02e8f1e
Signed-off-by: Jingwei Zhang <jingwei.zhang@intel.com>
Signed-off-by: Mingwei Shi <mingwei.shi@intel.com>
-rw-r--r-- | expectations/knownfailures.txt | 9 | ||||
-rwxr-xr-x[-rw-r--r--] | luni/src/main/native/java_lang_StringToReal.cpp | 151 | ||||
-rw-r--r-- | luni/src/test/java/libcore/java/lang/FloatTest.java | 16 |
3 files changed, 33 insertions, 143 deletions
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt index d85df07..c6d3dd7 100644 --- a/expectations/knownfailures.txt +++ b/expectations/knownfailures.txt @@ -1469,15 +1469,6 @@ ] }, { - description: "Differences between glibc and bionic.", - modes: [host], - bug: 18087920, - result: EXEC_FAILED, - names: [ - "libcore.java.lang.DoubleTest#testParseLargestSubnormalDoublePrecision" - ] -}, -{ description: "Tests failing on host - needing investigation.", modes: [host], bug: 18547404, diff --git a/luni/src/main/native/java_lang_StringToReal.cpp b/luni/src/main/native/java_lang_StringToReal.cpp index 108f939..d1902af 100644..100755 --- a/luni/src/main/native/java_lang_StringToReal.cpp +++ b/luni/src/main/native/java_lang_StringToReal.cpp @@ -25,9 +25,6 @@ #include "cbigint.h" /* ************************* Defines ************************* */ -#if defined(__linux__) || defined(__APPLE__) -#define USE_LL -#endif #define LOW_I32_FROM_VAR(u64) LOW_I32_FROM_LONG64(u64) #define LOW_I32_FROM_PTR(u64ptr) LOW_I32_FROM_LONG64_PTR(u64ptr) @@ -38,69 +35,13 @@ #define DEFAULT_DOUBLE_WIDTH MAX_DOUBLE_ACCURACY_WIDTH -#if defined(USE_LL) #define DOUBLE_INFINITE_LONGBITS (0x7FF0000000000000LL) -#else -#if defined(USE_L) -#define DOUBLE_INFINITE_LONGBITS (0x7FF0000000000000L) -#else -#define DOUBLE_INFINITE_LONGBITS (0x7FF0000000000000) -#endif /* USE_L */ -#endif /* USE_LL */ #define DOUBLE_MINIMUM_LONGBITS (0x1) -#if defined(USE_LL) #define DOUBLE_MANTISSA_MASK (0x000FFFFFFFFFFFFFLL) #define DOUBLE_EXPONENT_MASK (0x7FF0000000000000LL) #define DOUBLE_NORMAL_MASK (0x0010000000000000LL) -#else -#if defined(USE_L) -#define DOUBLE_MANTISSA_MASK (0x000FFFFFFFFFFFFFL) -#define DOUBLE_EXPONENT_MASK (0x7FF0000000000000L) -#define DOUBLE_NORMAL_MASK (0x0010000000000000L) -#else -#define DOUBLE_MANTISSA_MASK (0x000FFFFFFFFFFFFF) -#define DOUBLE_EXPONENT_MASK (0x7FF0000000000000) -#define DOUBLE_NORMAL_MASK (0x0010000000000000) -#endif /* USE_L */ -#endif /* USE_LL */ - -/* Keep a count of the number of times we decrement and increment to - * approximate the double, and attempt to detect the case where we - * could potentially toggle back and forth between decrementing and - * incrementing. It is possible for us to be stuck in the loop when - * incrementing by one or decrementing by one may exceed or stay below - * the value that we are looking for. In this case, just break out of - * the loop if we toggle between incrementing and decrementing for more - * than twice. - */ -#define INCREMENT_DOUBLE(_x, _decCount, _incCount) \ - { \ - ++DOUBLE_TO_LONGBITS(_x); \ - _incCount++; \ - if( (_incCount > 2) && (_decCount > 2) ) { \ - if( _decCount > _incCount ) { \ - DOUBLE_TO_LONGBITS(_x) += _decCount - _incCount; \ - } else if( _incCount > _decCount ) { \ - DOUBLE_TO_LONGBITS(_x) -= _incCount - _decCount; \ - } \ - break; \ - } \ - } -#define DECREMENT_DOUBLE(_x, _decCount, _incCount) \ - { \ - --DOUBLE_TO_LONGBITS(_x); \ - _decCount++; \ - if( (_incCount > 2) && (_decCount > 2) ) { \ - if( _decCount > _incCount ) { \ - DOUBLE_TO_LONGBITS(_x) += _decCount - _incCount; \ - } else if( _incCount > _decCount ) { \ - DOUBLE_TO_LONGBITS(_x) -= _incCount - _decCount; \ - } \ - break; \ - } \ - } #define allocateU64(x, n) if (!((x) = reinterpret_cast<uint64_t*>(malloc((n) * sizeof(uint64_t))))) goto OutOfMemory; @@ -248,7 +189,6 @@ static jdouble createDouble(JNIEnv* env, const char* s, jint e) { } return result; - } static jdouble createDouble1(JNIEnv* env, uint64_t* f, int32_t length, jint e) { @@ -310,7 +250,6 @@ static jdouble createDouble1(JNIEnv* env, uint64_t* f, int32_t length, jint e) { first and let it fall to zero if need be. */ if (result == 0.0) - DOUBLE_TO_LONGBITS (result) = DOUBLE_MINIMUM_LONGBITS; return doubleAlgorithm (env, f, length, e, result); @@ -323,15 +262,6 @@ static jdouble createDouble1(JNIEnv* env, uint64_t* f, int32_t length, jint e) { * Clinger, Proceedings of the ACM SIGPLAN '90 Conference on * Programming Language Design and Implementation, June 20-22, * 1990, pp. 92-101. - * - * There is a possibility that the function will end up in an endless - * loop if the given approximating floating-point number (a very small - * floating-point whose value is very close to zero) straddles between - * two approximating integer values. We modified the algorithm slightly - * to detect the case where it oscillates back and forth between - * incrementing and decrementing the floating-point approximation. It - * is currently set such that if the oscillation occurs more than twice - * then return the original approximation. */ static jdouble doubleAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, jdouble z) { uint64_t m; @@ -340,11 +270,10 @@ static jdouble doubleAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, uint64_t* y; uint64_t* D; uint64_t* D2; - int32_t xLength, yLength, DLength, D2Length, decApproxCount, incApproxCount; + int32_t xLength, yLength, DLength, D2Length; x = y = D = D2 = 0; xLength = yLength = DLength = D2Length = 0; - decApproxCount = incApproxCount = 0; do { @@ -443,12 +372,13 @@ static jdouble doubleAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, comparison2 = compareHighPrecision (D2, D2Length, y, yLength); if (comparison2 < 0) { - if (comparison < 0 && m == DOUBLE_NORMAL_MASK) + if (comparison < 0 && m == DOUBLE_NORMAL_MASK + && DOUBLE_TO_LONGBITS(z) != DOUBLE_NORMAL_MASK) { simpleShiftLeftHighPrecision (D2, D2Length, 1); if (compareHighPrecision (D2, D2Length, y, yLength) > 0) { - DECREMENT_DOUBLE (z, decApproxCount, incApproxCount); + --DOUBLE_TO_LONGBITS (z); } else { @@ -466,7 +396,7 @@ static jdouble doubleAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, { if (comparison < 0 && m == DOUBLE_NORMAL_MASK) { - DECREMENT_DOUBLE (z, decApproxCount, incApproxCount); + --DOUBLE_TO_LONGBITS (z); } else { @@ -475,24 +405,24 @@ static jdouble doubleAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, } else if (comparison < 0) { - DECREMENT_DOUBLE (z, decApproxCount, incApproxCount); + --DOUBLE_TO_LONGBITS (z); break; } else { - INCREMENT_DOUBLE (z, decApproxCount, incApproxCount); + ++DOUBLE_TO_LONGBITS (z); break; } } else if (comparison < 0) { - DECREMENT_DOUBLE (z, decApproxCount, incApproxCount); + --DOUBLE_TO_LONGBITS (z); } else { if (DOUBLE_TO_LONGBITS (z) == DOUBLE_INFINITE_LONGBITS) break; - INCREMENT_DOUBLE (z, decApproxCount, incApproxCount); + ++DOUBLE_TO_LONGBITS (z); } } while (1); @@ -547,42 +477,6 @@ static const uint32_t float_tens[] = { #define FLOAT_EXPONENT_MASK (0x7F800000) #define FLOAT_NORMAL_MASK (0x00800000) -/* Keep a count of the number of times we decrement and increment to - * approximate the double, and attempt to detect the case where we - * could potentially toggle back and forth between decrementing and - * incrementing. It is possible for us to be stuck in the loop when - * incrementing by one or decrementing by one may exceed or stay below - * the value that we are looking for. In this case, just break out of - * the loop if we toggle between incrementing and decrementing for more - * than twice. - */ -#define INCREMENT_FLOAT(_x, _decCount, _incCount) \ - { \ - ++FLOAT_TO_INTBITS(_x); \ - _incCount++; \ - if( (_incCount > 2) && (_decCount > 2) ) { \ - if( _decCount > _incCount ) { \ - FLOAT_TO_INTBITS(_x) += _decCount - _incCount; \ - } else if( _incCount > _decCount ) { \ - FLOAT_TO_INTBITS(_x) -= _incCount - _decCount; \ - } \ - break; \ - } \ - } -#define DECREMENT_FLOAT(_x, _decCount, _incCount) \ - { \ - --FLOAT_TO_INTBITS(_x); \ - _decCount++; \ - if( (_incCount > 2) && (_decCount > 2) ) { \ - if( _decCount > _incCount ) { \ - FLOAT_TO_INTBITS(_x) += _decCount - _incCount; \ - } else if( _incCount > _decCount ) { \ - FLOAT_TO_INTBITS(_x) -= _incCount - _decCount; \ - } \ - break; \ - } \ - } - static jfloat createFloat(JNIEnv* env, const char* s, jint e) { /* assumes s is a null terminated string with at least one * character in it */ @@ -682,7 +576,6 @@ static jfloat createFloat(JNIEnv* env, const char* s, jint e) { } return result; - } static jfloat createFloat1 (JNIEnv* env, uint64_t* f, int32_t length, jint e) { @@ -796,15 +689,6 @@ static jfloat createFloat1 (JNIEnv* env, uint64_t* f, int32_t length, jint e) { * Clinger, Proceedings of the ACM SIGPLAN '90 Conference on * Programming Language Design and Implementation, June 20-22, * 1990, pp. 92-101. - * - * There is a possibility that the function will end up in an endless - * loop if the given approximating floating-point number (a very small - * floating-point whose value is very close to zero) straddles between - * two approximating integer values. We modified the algorithm slightly - * to detect the case where it oscillates back and forth between - * incrementing and decrementing the floating-point approximation. It - * is currently set such that if the oscillation occurs more than twice - * then return the original approximation. */ static jfloat floatAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, jfloat z) { uint64_t m; @@ -814,11 +698,9 @@ static jfloat floatAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, j uint64_t* D; uint64_t* D2; int32_t xLength, yLength, DLength, D2Length; - int32_t decApproxCount, incApproxCount; x = y = D = D2 = 0; xLength = yLength = DLength = D2Length = 0; - decApproxCount = incApproxCount = 0; do { @@ -917,12 +799,13 @@ static jfloat floatAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, j comparison2 = compareHighPrecision (D2, D2Length, y, yLength); if (comparison2 < 0) { - if (comparison < 0 && m == FLOAT_NORMAL_MASK) + if (comparison < 0 && m == FLOAT_NORMAL_MASK + && FLOAT_TO_INTBITS(z) != FLOAT_NORMAL_MASK) { simpleShiftLeftHighPrecision (D2, D2Length, 1); if (compareHighPrecision (D2, D2Length, y, yLength) > 0) { - DECREMENT_FLOAT (z, decApproxCount, incApproxCount); + --FLOAT_TO_INTBITS (z); } else { @@ -940,7 +823,7 @@ static jfloat floatAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, j { if (comparison < 0 && m == FLOAT_NORMAL_MASK) { - DECREMENT_FLOAT (z, decApproxCount, incApproxCount); + --FLOAT_TO_INTBITS (z); } else { @@ -949,24 +832,24 @@ static jfloat floatAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, j } else if (comparison < 0) { - DECREMENT_FLOAT (z, decApproxCount, incApproxCount); + --FLOAT_TO_INTBITS (z); break; } else { - INCREMENT_FLOAT (z, decApproxCount, incApproxCount); + ++FLOAT_TO_INTBITS (z); break; } } else if (comparison < 0) { - DECREMENT_FLOAT (z, decApproxCount, incApproxCount); + --FLOAT_TO_INTBITS (z); } else { if (FLOAT_TO_INTBITS (z) == FLOAT_EXPONENT_MASK) break; - INCREMENT_FLOAT (z, decApproxCount, incApproxCount); + ++FLOAT_TO_INTBITS (z); } } while (1); diff --git a/luni/src/test/java/libcore/java/lang/FloatTest.java b/luni/src/test/java/libcore/java/lang/FloatTest.java index 92e7ae4..c25bd5c 100644 --- a/luni/src/test/java/libcore/java/lang/FloatTest.java +++ b/luni/src/test/java/libcore/java/lang/FloatTest.java @@ -121,4 +121,20 @@ public class FloatTest extends junit.framework.TestCase { } assertEquals(f1, 0f); } + + // Float equivalent of testParseLargestSubnormalDoublePrecision. http://b/18087920. + public void testParseLargestSubnormalFloatPrecision() { + // These are different ways of saying MIN_NORMAL. + assertEquals(1.1754943508222875e-38f, Float.parseFloat("1.1754943508222875e-38")); + assertEquals(1.1754943508222875e-38f, Float.parseFloat("0.00011754943508222875e-34f")); + assertEquals(1.1754943508222875e-38f, Float.parseFloat("00000001.1754943508222875e-38f")); + assertEquals(1.1754943508222875e-38f, Float.parseFloat("1.17549435082228750000e-38f")); + assertEquals(1.1754943508222875e-38f, Float.parseFloat("1.1754943508222875e-0038f")); + assertEquals(-1.1754943508222875e-38f, Float.parseFloat("-1.1754943508222875e-38f")); + + // Extra interesting values suggested as part of http://b/18087920. + assertEquals(1.1754944e-38f, Float.parseFloat("11754942807573643E-54")); + assertEquals(1.1754944e-38f, Float.parseFloat("11754942807573644E-54")); + assertEquals(1.1754944e-38f, Float.parseFloat("11754942807573645E-54")); + } } |