Selaa lähdekoodia

[java/cs] Use native Float parsing to ensure no precision loss. Closes #4051

Cauê Waneck 10 vuotta sitten
vanhempi
commit
9ba87941bf
3 muutettua tiedostoa jossa 75 lisäystä ja 178 poistoa
  1. 31 85
      std/cs/_std/Std.hx
  2. 35 93
      std/java/_std/Std.hx
  3. 9 0
      tests/unit/src/unit/issues/Issue4051.hx

+ 31 - 85
std/cs/_std/Std.hx

@@ -134,103 +134,49 @@ import cs.internal.Exceptions;
 	public static function parseFloat( x : String ) : Float {
 		if (x == null) return Math.NaN;
 		x = StringTools.ltrim(x);
-
-		var ret = 0.0;
-		var div = 0.0;
-		var e = 0.0;
-
-		var len = x.length;
-		var foundAny = false;
-		var isNeg = false;
+		var found = false, isHex = false, hasDot = false, hasE = false, hasNeg = false, hasENeg = false;
 		var i = -1;
-		while (++i < len)
-		{
-			var c = cast(untyped x[i], Int); //fastCodeAt
-			if (!foundAny)
-			{
-				switch(c)
-				{
-					case '-'.code:
-						isNeg = true;
-						continue;
-					case ' '.code, '\t'.code, '\n'.code, '\r'.code, '+'.code:
-						if (isNeg)
-							return Math.NaN;
-						continue;
-				}
-			}
+		inline function getch(i:Int):Int return cast ((untyped x : cs.system.String)[i]);
 
-			if (c == '.'.code)
-			{
-				if (div != 0.0)
-					break;
-				div = 1.0;
-				foundAny = true;
-
-				continue;
-			}
-
-			if (c >= '0'.code && c <= '9'.code)
+		while (++i < x.length)
+		{
+			var chr = getch(i);
+			if (chr >= '0'.code && chr <= '9'.code)
 			{
-				if (!foundAny && c == '0'.code)
+				if ( !found && chr == '0'.code && (i+1) < x.length )
 				{
-					foundAny = true;
-					continue;
-				}
-
-				ret *= 10; foundAny = true; div *= 10;
-
-				ret += c - '0'.code;
-			} else if (foundAny && (c == 'e'.code || c == 'E'.code)) {
-				var eNeg = false;
-				var eFoundAny = false;
-				if (i + 1 < len)
-				{
-					var next = untyped cast(x[i + 1], Int);
-					if (next == '-'.code)
+					var next = getch(i+1);
+					if (next == 'x'.code || next == 'X'.code)
 					{
-						eNeg = true;
-						i++;
-					} else if (next == '+'.code) {
+						isHex = true;
 						i++;
 					}
 				}
-
-				while (++i < len)
-				{
-					c = untyped cast(x[i], Int);
-					if (c >= '0'.code && c <= '9'.code)
-					{
-						if (!eFoundAny && c == '0'.code)
-							continue;
-						eFoundAny = true;
-						e *= 10;
-						e += c - '0'.code;
-					} else {
-						break;
-					}
-				}
-
-				if (eNeg) e = -e;
-			} else {
-				break;
+				found = true;
+			} else switch (chr) {
+				case 'a'.code | 'b'.code | 'c'.code | 'd'.code | 'e'.code | 'f'.code
+				   | 'A'.code | 'B'.code | 'C'.code | 'D'.code | 'E'.code | 'F'.code if (isHex):
+					//do nothing - it's alright
+				case 'e'.code | 'E'.code if(!hasE):
+					hasE = true;
+				case '.'.code if (!hasDot):
+					hasDot = true;
+				case '-'.code if (!found && !hasNeg):
+					hasNeg = true;
+				case '-'.code if (found && !hasENeg && hasE):
+					hasENeg = true;
+				case _:
+					break;
 			}
 		}
-
-		if (div == 0.0) div = 1.0;
-
-		if (foundAny)
+		if (i != x.length)
 		{
-			ret = isNeg ? -(ret / div) : (ret / div);
-			if (e != 0.0)
-			{
-				return ret * Math.pow(10.0, e);
-			} else {
-				return ret;
-			}
-		} else {
-			return Math.NaN;
+			x = x.substr(0,i);
 		}
+		return try
+			cs.system.Double.Parse(x, (null : cs.system.IFormatProvider))
+		catch(e:Dynamic)
+			Math.NaN;
 	}
 
 	@:extern inline public static function instance<T:{},S:T>( value : T, c : Class<S> ) : S {

+ 35 - 93
std/java/_std/Std.hx

@@ -91,7 +91,7 @@ import java.internal.Exceptions;
 					case \'-\':
 						isNeg = true;
 						continue;
-          case \'+\':
+					case \'+\':
 					case \'\\n\':
 					case \'\\t\':
 					case \'\\r\':
@@ -135,110 +135,52 @@ import java.internal.Exceptions;
 		return null;
 	}
 
-	@:functionCode('
-		if (x == null) return java.lang.Double.NaN;
-
-		x = x.trim();
-		double ret = 0.0;
-		double div = 0.0;
-		double e = 0.0;
+	public static function parseFloat( x : String ) : Float {
+		if (x == null) return Math.NaN;
+		x = StringTools.ltrim(x);
+		var found = false, isHex = false, hasDot = false, hasE = false, hasNeg = false, hasENeg = false;
+		var i = -1;
+		inline function getch(i:Int):Int return cast (untyped x._charAt(i) : java.StdTypes.Char16);
 
-		int len = x.length();
-		boolean foundAny = false;
-		boolean isNeg = false;
-		for (int i = 0; i < len; i++)
+		while (++i < x.length)
 		{
-			char c = x.charAt(i);
-			if (!foundAny)
+			var chr = getch(i);
+			if (chr >= '0'.code && chr <= '9'.code)
 			{
-				switch(c)
+				if ( !found && chr == '0'.code && (i+1) < x.length )
 				{
-					case \'-\':
-						isNeg = true;
-						continue;
-          case \'+\':
-					case \'\\n\':
-					case \'\\t\':
-					case \'\\r\':
-					case \' \':
-					if (isNeg) return java.lang.Double.NaN;
-						continue;
-				}
-			}
-
-			if (c == \'.\') {
-				if (div != 0.0)
-					break;
-				div = 1.0;
-				foundAny = true;
-
-				continue;
-			}
-
-			if (c >= \'0\' && c <= \'9\')
-			{
-				if (!foundAny && c == \'0\')
-				{
-					foundAny = true;
-					continue;
-				}
-				ret *= 10.0; foundAny = true; div *= 10.0;
-
-				ret += ((int) (c - \'0\'));
-			} else if (foundAny && c == \'E\' || c == \'e\') {
-				boolean eNeg = false;
-				boolean eFoundAny = false;
-
-				char next = x.charAt(i + 1);
-				if (i + 1 < len)
-				{
-					if (next == \'-\')
+					var next = getch(i+1);
+					if (next == 'x'.code || next == 'X'.code)
 					{
-						eNeg = true;
-						i++;
-					} else if (next == \'+\') {
+						isHex = true;
 						i++;
 					}
 				}
-
-				while (++i < len)
-				{
-					c = x.charAt(i);
-					if (c >= \'0\' && c <= \'9\')
-					{
-						if (!eFoundAny && c == \'0\')
-							continue;
-						eFoundAny = true;
-						e *= 10.0;
-						e += ((int) (c - \'0\'));
-					} else {
-						break;
-					}
-				}
-
-				if (eNeg) e = -e;
-			} else {
-				break;
+				found = true;
+			} else switch (chr) {
+				case 'a'.code | 'b'.code | 'c'.code | 'd'.code | 'e'.code | 'f'.code
+				   | 'A'.code | 'B'.code | 'C'.code | 'D'.code | 'E'.code | 'F'.code if (isHex):
+					//do nothing - it's alright
+				case 'e'.code | 'E'.code if(!hasE):
+					hasE = true;
+				case '.'.code if (!hasDot):
+					hasDot = true;
+				case '-'.code if (!found && !hasNeg):
+					hasNeg = true;
+				case '-'.code if (found && !hasENeg && hasE):
+					hasENeg = true;
+				case _:
+					break;
 			}
 		}
-
-		if (div == 0.0) div = 1.0;
-
-		if (foundAny)
+		if (i != x.length)
 		{
-			ret = isNeg ? -(ret / div) : (ret / div);
-			if (e != 0.0)
-			{
-				return ret * Math.pow(10.0, e);
-			} else {
-				return ret;
-			}
-		} else {
-			return java.lang.Double.NaN;
+			x = x.substr(0,i);
 		}
-	')
-	public static function parseFloat( x : String ) : Float {
-		return 0.0;
+		return try
+			java.lang.Double.DoubleClass.parseDouble(x)
+		catch(e:Dynamic)
+			Math.NaN;
 	}
 
 	inline public static function instance<T:{},S:T>( value : T, c : Class<S> ) : S {

+ 9 - 0
tests/unit/src/unit/issues/Issue4051.hx

@@ -0,0 +1,9 @@
+package unit.issues;
+
+class Issue4051 extends Test
+{
+	public function test()
+	{
+		eq(Std.parseFloat("1154874.2868745863"), 1154874.2868745863);
+	}
+}