Fix Double.parseDouble and Float.parseFloat for some cases of illegal input

This commit is contained in:
Alexey Andreev 2019-08-12 16:38:52 +03:00
parent c5334e344d
commit 1d19562c49
5 changed files with 118 additions and 64 deletions

View File

@ -77,15 +77,32 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
public static double parseDouble(TString string) throws TNumberFormatException { public static double parseDouble(TString string) throws TNumberFormatException {
// TODO: parse infinite and different radix // TODO: parse infinite and different radix
string = string.trim();
if (string.isEmpty()) {
throw new TNumberFormatException();
}
int start = 0;
int end = string.length();
while (string.charAt(start) <= ' ') {
if (++start == end) {
throw new TNumberFormatException();
}
}
while (string.charAt(end - 1) <= ' ') {
--end;
}
boolean negative = false; boolean negative = false;
int index = 0; int index = start;
if (string.charAt(index) == '-') { if (string.charAt(index) == '-') {
++index; ++index;
negative = true; negative = true;
} else if (string.charAt(index) == '+') { } else if (string.charAt(index) == '+') {
++index; ++index;
} }
if (index == end) {
throw new TNumberFormatException();
}
char c = string.charAt(index); char c = string.charAt(index);
long mantissa = 0; long mantissa = 0;
@ -96,12 +113,10 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
if (c < '0' || c > '9') { if (c < '0' || c > '9') {
throw new TNumberFormatException(); throw new TNumberFormatException();
} }
while (string.charAt(index) == '0') { while (index < end && string.charAt(index) == '0') {
if (++index == string.length()) { ++index;
return 0;
} }
} while (index < end) {
while (index < string.length()) {
c = string.charAt(index); c = string.charAt(index);
if (c < '0' || c > '9') { if (c < '0' || c > '9') {
break; break;
@ -114,9 +129,9 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
++index; ++index;
} }
} }
if (index < string.length() && string.charAt(index) == '.') { if (index < end && string.charAt(index) == '.') {
++index; ++index;
while (index < string.length()) { while (index < end) {
c = string.charAt(index); c = string.charAt(index);
if (c < '0' || c > '9') { if (c < '0' || c > '9') {
break; break;
@ -132,13 +147,16 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
throw new TNumberFormatException(); throw new TNumberFormatException();
} }
} }
if (index < string.length()) { if (index < end) {
c = string.charAt(index); c = string.charAt(index);
if (c != 'e' && c != 'E') { if (c != 'e' && c != 'E') {
throw new TNumberFormatException(); throw new TNumberFormatException();
} }
++index; ++index;
boolean negativeExp = false; boolean negativeExp = false;
if (index == end) {
throw new TNumberFormatException();
}
if (string.charAt(index) == '-') { if (string.charAt(index) == '-') {
++index; ++index;
negativeExp = true; negativeExp = true;
@ -147,7 +165,7 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
} }
int numExp = 0; int numExp = 0;
hasOneDigit = false; hasOneDigit = false;
while (index < string.length()) { while (index < end) {
c = string.charAt(index); c = string.charAt(index);
if (c < '0' || c > '9') { if (c < '0' || c > '9') {
break; break;

View File

@ -121,15 +121,32 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
public static float parseFloat(TString string) throws TNumberFormatException { public static float parseFloat(TString string) throws TNumberFormatException {
// TODO: parse infinite and different radix // TODO: parse infinite and different radix
string = string.trim();
if (string.isEmpty()) {
throw new TNumberFormatException();
}
int start = 0;
int end = string.length();
while (string.charAt(start) <= ' ') {
if (++start == end) {
throw new TNumberFormatException();
}
}
while (string.charAt(end - 1) <= ' ') {
--end;
}
boolean negative = false; boolean negative = false;
int index = 0; int index = start;
if (string.charAt(index) == '-') { if (string.charAt(index) == '-') {
++index; ++index;
negative = true; negative = true;
} else if (string.charAt(index) == '+') { } else if (string.charAt(index) == '+') {
++index; ++index;
} }
if (index == end) {
throw new TNumberFormatException();
}
char c = string.charAt(index); char c = string.charAt(index);
int mantissa = 0; int mantissa = 0;
@ -142,12 +159,10 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
throw new TNumberFormatException(); throw new TNumberFormatException();
} }
while (string.charAt(index) == '0') { while (index < end && string.charAt(index) == '0') {
if (++index == string.length()) { ++index;
return 0;
} }
} while (index < end) {
while (index < string.length()) {
c = string.charAt(index); c = string.charAt(index);
if (c < '0' || c > '9') { if (c < '0' || c > '9') {
break; break;
@ -161,9 +176,9 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
} }
} }
if (index < string.length() && string.charAt(index) == '.') { if (index < end && string.charAt(index) == '.') {
++index; ++index;
while (index < string.length()) { while (index < end) {
c = string.charAt(index); c = string.charAt(index);
if (c < '0' || c > '9') { if (c < '0' || c > '9') {
break; break;
@ -179,13 +194,16 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
throw new TNumberFormatException(); throw new TNumberFormatException();
} }
} }
if (index < string.length()) { if (index < end) {
c = string.charAt(index); c = string.charAt(index);
if (c != 'e' && c != 'E') { if (c != 'e' && c != 'E') {
throw new TNumberFormatException(); throw new TNumberFormatException();
} }
++index; ++index;
boolean negativeExp = false; boolean negativeExp = false;
if (index == end) {
throw new TNumberFormatException();
}
if (string.charAt(index) == '-') { if (string.charAt(index) == '-') {
++index; ++index;
negativeExp = true; negativeExp = true;
@ -194,7 +212,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
} }
int numExp = 0; int numExp = 0;
hasOneDigit = false; hasOneDigit = false;
while (index < string.length()) { while (index < end) {
c = string.charAt(index); c = string.charAt(index);
if (c < '0' || c > '9') { if (c < '0' || c > '9') {
break; break;

View File

@ -17,6 +17,7 @@ package org.teavm.classlib.java.lang;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.TeaVMTestRunner;
@ -37,43 +38,51 @@ public class DoubleTest {
assertEquals(23, Double.parseDouble("23."), 1E-12); assertEquals(23, Double.parseDouble("23."), 1E-12);
assertEquals(0.1, Double.parseDouble("0.1"), 0.001); assertEquals(0.1, Double.parseDouble("0.1"), 0.001);
assertEquals(0.1, Double.parseDouble(".1"), 0.001); assertEquals(0.1, Double.parseDouble(".1"), 0.001);
} assertEquals(0.1, Double.parseDouble(" .1"), 0.001);
assertEquals(0.1, Double.parseDouble(".1 "), 0.001);
@Test
public void negativeParsed() {
assertEquals(-23, Double.parseDouble("-23"), 1E-12); assertEquals(-23, Double.parseDouble("-23"), 1E-12);
}
@Test
public void zeroParsed() {
assertEquals(0, Double.parseDouble("0.0"), 1E-12); assertEquals(0, Double.parseDouble("0.0"), 1E-12);
assertEquals(0, Double.parseDouble("0"), 1E-12);
assertEquals(0, Double.parseDouble("00"), 1E-12);
assertEquals(0, Double.parseDouble("0."), 1E-12);
assertEquals(0, Double.parseDouble(".0"), 1E-12);
assertEquals(0, Double.parseDouble("23E-8000"), 1E-12); assertEquals(0, Double.parseDouble("23E-8000"), 1E-12);
assertEquals(0, Double.parseDouble("00000"), 1E-12); assertEquals(0, Double.parseDouble("00000"), 1E-12);
assertEquals(0, Double.parseDouble("00000.0000"), 1E-12); assertEquals(0, Double.parseDouble("00000.0000"), 1E-12);
} }
@Test
public void parsedWithError() {
checkIllegalFormat("");
checkIllegalFormat(" ");
checkIllegalFormat("a");
checkIllegalFormat(" a ");
checkIllegalFormat("-");
checkIllegalFormat("-.");
checkIllegalFormat(".");
checkIllegalFormat("1e-");
checkIllegalFormat("1e");
}
private void checkIllegalFormat(String string) {
try {
Double.parseDouble(string);
fail("Exception expected parsing string: " + string);
} catch (NumberFormatException e) {
// It's expected
}
}
@Test @Test
public void longBitsExtracted() { public void longBitsExtracted() {
assertEquals(0x41E23456789ABCDEL, Double.doubleToLongBits(0x1.23456789ABCDEP+31)); assertEquals(0x41E23456789ABCDEL, Double.doubleToLongBits(0x1.23456789ABCDEP+31));
}
@Test
public void longBitsExtracted2() {
assertEquals(0x3FE1C28F5C28F5C3L >>> 3, Double.doubleToLongBits(0.555) >>> 3); assertEquals(0x3FE1C28F5C28F5C3L >>> 3, Double.doubleToLongBits(0.555) >>> 3);
}
@Test
public void subNormalLongBitsExtracted() {
assertEquals(0x00000056789ABCDEL, Double.doubleToLongBits(0x0.00056789ABCDEP-1022)); assertEquals(0x00000056789ABCDEL, Double.doubleToLongBits(0x0.00056789ABCDEP-1022));
} }
@Test @Test
public void longBitsPacked() { public void longBitsPacked() {
assertEquals(0x1.23456789ABCDEP+31, Double.longBitsToDouble(0x41E23456789ABCDEL), 0x1.0P-19); assertEquals(0x1.23456789ABCDEP+31, Double.longBitsToDouble(0x41E23456789ABCDEL), 0x1.0P-19);
}
@Test
public void subNormalLongBitsPacked() {
assertEquals(0x0.00056789ABCDEP-1022, Double.longBitsToDouble(0x00000056789ABCDEL), 0x1.0P-19); assertEquals(0x0.00056789ABCDEP-1022, Double.longBitsToDouble(0x00000056789ABCDEL), 0x1.0P-19);
} }

View File

@ -17,6 +17,7 @@ package org.teavm.classlib.java.lang;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.TeaVMTestRunner;
@ -38,43 +39,51 @@ public class FloatTest {
assertEquals(23, Float.parseFloat("23."), 1E-12F); assertEquals(23, Float.parseFloat("23."), 1E-12F);
assertEquals(0.1F, Float.parseFloat("0.1"), 0.001F); assertEquals(0.1F, Float.parseFloat("0.1"), 0.001F);
assertEquals(0.1F, Float.parseFloat(".1"), 0.001F); assertEquals(0.1F, Float.parseFloat(".1"), 0.001F);
} assertEquals(0.1F, Float.parseFloat(" .1"), 0.001F);
assertEquals(0.1F, Float.parseFloat(".1 "), 0.001F);
@Test
public void negativeParsed() {
assertEquals(-23, Float.parseFloat("-23"), 1E-12F); assertEquals(-23, Float.parseFloat("-23"), 1E-12F);
}
@Test
public void zeroParsed() {
assertEquals(0, Float.parseFloat("0.0"), 1E-12F); assertEquals(0, Float.parseFloat("0.0"), 1E-12F);
assertEquals(0, Float.parseFloat("0"), 1E-12F);
assertEquals(0, Float.parseFloat("00"), 1E-12F);
assertEquals(0, Float.parseFloat(".0"), 1E-12F);
assertEquals(0, Float.parseFloat("0."), 1E-12F);
assertEquals(0, Float.parseFloat("23E-8000"), 1E-12F); assertEquals(0, Float.parseFloat("23E-8000"), 1E-12F);
assertEquals(0, Float.parseFloat("00000"), 1E-12F); assertEquals(0, Float.parseFloat("00000"), 1E-12F);
assertEquals(0, Float.parseFloat("00000.0000"), 1E-12F); assertEquals(0, Float.parseFloat("00000.0000"), 1E-12F);
} }
@Test
public void parsedWithError() {
checkIllegalFormat("");
checkIllegalFormat(" ");
checkIllegalFormat("a");
checkIllegalFormat(" a ");
checkIllegalFormat("-");
checkIllegalFormat("-.");
checkIllegalFormat(".");
checkIllegalFormat("1e-");
checkIllegalFormat("1e");
}
private void checkIllegalFormat(String string) {
try {
Float.parseFloat(string);
fail("Exception expected parsing string: " + string);
} catch (NumberFormatException e) {
// It's expected
}
}
@Test @Test
public void floatBitsExtracted() { public void floatBitsExtracted() {
assertEquals(0x4591A2B4, Float.floatToIntBits(0x1.234567p+12f)); assertEquals(0x4591A2B4, Float.floatToIntBits(0x1.234567p+12f));
}
@Test
public void floatBitsExtracted2() {
assertEquals(0x800000, Float.floatToIntBits((float) Math.pow(2, -126))); assertEquals(0x800000, Float.floatToIntBits((float) Math.pow(2, -126)));
}
@Test
public void subNormalFloatBitsExtracted() {
assertEquals(0x000092, Float.floatToIntBits(0x0.000123p-126f)); assertEquals(0x000092, Float.floatToIntBits(0x0.000123p-126f));
} }
@Test @Test
public void floatBitsPacked() { public void floatBitsPacked() {
assertEquals(0x1.234567p+12f, Float.intBitsToFloat(0x4591A2B4), 1e7); assertEquals(0x1.234567p+12f, Float.intBitsToFloat(0x4591A2B4), 1e7);
}
@Test
public void subNormalFloatBitsPacked() {
assertEquals(0x0.000123p-126f, Float.intBitsToFloat(0x000092), 0x000008p-126); assertEquals(0x0.000123p-126f, Float.intBitsToFloat(0x000092), 0x000008p-126);
} }

View File

@ -125,8 +125,8 @@ public class SystemTest {
System.err.println("err overridden"); System.err.println("err overridden");
System.err.flush(); System.err.flush();
assertEquals("err overridden\n", new String(err.toByteArray())); assertEquals("err overridden" + System.lineSeparator(), new String(err.toByteArray()));
assertEquals("out overridden\n", new String(out.toByteArray())); assertEquals("out overridden" + System.lineSeparator(), new String(out.toByteArray()));
} }
@Test @Test