ceylon.language.formatFloat.ceylon Maven / Gradle / Ivy
"The string decimal representation of the given
[[floating point number|float]]. If the given number is
[[negative|Float.negative]], the string representation will
begin with `-`. The [[whole part|Float.wholePart]] and
[[fractional parts|Float.fractionalPart]] of the number are
separated by a `.` decimal point. Digits consist of decimal
digits `0` to `9`.
The number of decimal places following the decimal point is
controlled by the parameters [[minDecimalPlaces]] and
[[maxDecimalPlaces]], which default to `1` and `9`
respectively, so that by default the string representation
always contains a decimal point, and never contains more
than nine decimal places. The decimal representation is
truncated so that the number of decimal places never
exceeds the specified maximum.
For example:
- `formatFloat(1234.1234)` is `\"1234.1234\"`
- `formatFloat(0.1234)` is `\"0.1234\"`
- `formatFloat(1234.0)` is `\"1234.0\"`
- `formatFloat(1234.0,0)` is `\"1234\"`
- `formatFloat(1234.1234,6)` is `\"1234.123400\"`
- `formatFloat(1234.1234,0,2)` is `\"1234.12\"`
- `formatFloat(0.0001,2,2)` is `\"0.00\"`
- `formatFloat(0.0001,0,2)` is `\"0\"`
Finally:
- `formatFloat(-0.0)` is `\"0.0\"`,
- `formatFloat(0.0/0)` is `\"NaN\"`,
- `formatFloat(1.0/0)` is `\"Infinity\"`, and
- `formatFloat(-1.0/0)` is `\"-Infinity\".`
This function never produces a representation involving
scientific notation."
tagged("Numbers")
see (`function formatInteger`,
`function parseFloat`)
shared String formatFloat(
"The floating point value to format."
Float float,
"The minimum number of allowed decimal places.
If `minDecimalPlaces<=0`, the result may have no
decimal point."
Integer minDecimalPlaces=1,
"The maximum number of allowed decimal places.
If `maxDecimalPlaces<=0`, the result always has no
decimal point."
Integer maxDecimalPlaces=9) {
if (float.undefined || float.infinite) {
return float.string;
}
Float magnitude = float.magnitude;
variable Integer i = maxDecimalPlaces;
Float maxExactIntegralFloat
= runtime.maxExactIntegralFloat.float;
while (i>0 &&
magnitude > maxExactIntegralFloat/10^i) {
//reduce the number of computed decimal places,
//so that magnitude * 10^i will fit into a Float
i--;
}
Integer decimalPlaces = i;
variable Boolean previousZero = false;
variable {Character*} digits = {};
while (true) {
Float scaled = scaleByPowerOfTen(magnitude, i);
Float fractional = scaled.fractionalPart;
Float whole = scaled.wholePart;
Integer d = (fractional * 10).integer;
Integer digit;
if (previousZero) {
digit =
//detect rounding error in floating point
//multiplication magnitude * 10^i above
(fractional * 100).integer > d * 10
then (d==9 then 0 else d + 1)
else d;
}
else {
digit = d;
}
Character c = (digit + zeroInt).character;
digits = digits.follow(c);
if (whole == 0.0) {
break;
}
previousZero = digit==0;
i--;
}
String string = String(digits.exceptLast);
Integer point = string.size - decimalPlaces;
String wholePart;
String fractionalPart;
if (point>0) {
wholePart = string[0:point];
fractionalPart = string[point...];
}
else {
wholePart = "0";
fractionalPart
= string.padLeading(maxDecimalPlaces, '0');
}
String normalized =
fractionalPart
.trimTrailing('0'.equals)
.padTrailing(minDecimalPlaces, '0');
String signed =
float.negative
then "-" + wholePart
else wholePart;
return normalized.empty
then signed
else signed + "." + normalized;
}
Float scaleByPowerOfTen(Float float, Integer power) {
Float scale =
let (magnitude = power.magnitude)
//there are only 15 decimal places of precision
//in IEEE double-precision floating point number
if (magnitude <= 15)
then (10^magnitude).nearestFloat //fast
else 10.0.powerOfInteger(magnitude); //slow
return if (power < 0)
then float / scale
else float * scale;
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy