"The [[Float]] value of the given
[[string representation|string]] of a decimal floating
point number, or `null` if the string does not represent a
decimal floating point number.
The syntax accepted by this method is the same as the
syntax for a `Float` literal in the Ceylon language
except that it may optionally begin with a sign
character (`+` or `-`) and may not contain grouping
underscore characters."
see (`function parseInteger`)
shared Float? parseFloat(String string) {
// parse the sign first
Integer sign;
String unsignedPart;
if (string.startsWith("-")) {
sign = -1;
unsignedPart = string[1...];
} else if (string.startsWith("+")) {
sign = +1;
unsignedPart = string[1...];
} else {
sign = +1;
unsignedPart = string;
}
// split into three main parts
String wholePart;
String fractionalPart;
String? rest;
if (exists dot = unsignedPart.firstOccurrence('.')) {
wholePart = unsignedPart[...dot-1];
String afterWholePart = unsignedPart[dot+1...];
if (exists mag
= afterWholePart.firstIndexWhere(Character.letter)) {
fractionalPart = afterWholePart[...mag-1];
rest = afterWholePart[mag...];
}
else {
fractionalPart = afterWholePart;
rest = null;
}
}
else {
if (exists mag
= unsignedPart.firstIndexWhere(Character.letter)) {
wholePart = unsignedPart[...mag-1];
rest = unsignedPart[mag...];
}
else {
wholePart = unsignedPart;
rest = null;
}
fractionalPart = "0";
}
if (!wholePart.every(Character.digit) ||
!fractionalPart.every(Character.digit)) {
return null;
}
if (exists whole = parseInteger(wholePart),
exists fractional = parseInteger(fractionalPart)) {
value shift = fractionalPart.size;
Integer exponent;
if (exists rest) {
if (exists magnitude
= parseFloatExponent(rest)) {
exponent = magnitude-shift;
}
else {
return null;
}
}
else {
exponent = -shift;
}
Integer numerator = whole*10^shift + fractional;
Float signedNumerator
= numerator.zero
then 0 * sign.float //preserve sign of -0.0
else (sign * numerator).float;
value exponentMagnitude = exponent.magnitude;
if (exponentMagnitude==0) {
return signedNumerator;
}
else if (exponentMagnitude<maximumIntegerExponent) {
value scale = 10^exponentMagnitude;
return exponent<0
then signedNumerator / scale
else signedNumerator * scale;
}
else {
//scale can't be represented as
//an integer, resulting in some
//rounding error
return signedNumerator * 10.0^exponent;
}
}
return null;
}
//TODO: replace with a native implementation
Integer maximumIntegerExponent
= smallest(runtime.maxIntegerValue.string.size,
runtime.minIntegerValue.string.size-1);
Integer? parseFloatExponent(String string) {
switch (string)
case ("k") {
return 3;
}
case ("M") {
return 6;
}
case ("G") {
return 9;
}
case ("T") {
return 12;
}
case ("P") {
return 15;
}
case ("m") {
return -3;
}
case ("u") {
return -6;
}
case ("n") {
return -9;
}
case ("p") {
return -12;
}
case ("f") {
return -15;
}
else {
if (string.lowercased.startsWith("e") &&
string.rest.every(digitOrSign)) {
return parseInteger(string.rest);
}
else {
return null;
}
}
}
Boolean(Character) digitOrSign
= or(Character.digit, "+-".contains);