Open DartBot opened 11 years ago
Removed Type-Defect label. Added Type-Enhancement, Area-Library, Triaged labels.
This comment was originally written by adrian.avila.mtz...@gmail.com
No plans for fixing this one yet?
Has been a while.
Looks like Dart already has this but it's named strangely, the docs are vague about the rounding part and it returns a string.
1.119.toStringAsFixed(2)
Results in 1.12
.
The docs should mention that this method actually does rounding and not simply truncate. Also, it would make more sense to add this capability to the existing num.round()
method instead of introducing this newfangled num.toStringAsFixed()
method.
num.round()
returns an int AFAIK and there is no way to specify the number of fraction digits in double. This would need something like the decimal number in core.
@zoechi num.toStringAsFixed()
does exactly what you'd expect from round()
in other languages. It's just difficult to discover this fact since the Dart docs for num.toStringAsFixed()
don't clearly state that it does rounding for you. I think most people would probably look at the existing num.round()
method and see it doesn't support decimals and not bother to look at the very poorly named num.toStringAsFixed()
rounding method.
@eukreign I don't argue against that. I think that it just can't be integrated into num.round()
because the return types differ.
What if I need a double with precision, not a string. i.e . need 1.00 or 2.00 . num = 1 double.parse(num.toStringAsFixed(2)) this will return 1 again. Why do we need toStringAsFixed(n) where we can call .toString on every type. Rather this is more important to have precision that return it's original type.
Yes toStringAsFixed
is for strings, no use for Maths. We need it for doubles - especially since flutter has it's peculiarities with like 85.0 + 0.1 = 85.1, but add another 0.1 and it equals 85.19999999 or whatever.
That's not a Flutter peculiarity. It's an IEEE-754 floating point peculiarity which is shared by all languages using normal floating points. The value of 85.0 + 0.1 + 0.1
is not the same double as 85.2
. It's not the same because the numerical value 0.1 is not exactly representable as a double value, and the two additions of inexact values causes the error to accumulate to the point where the difference is distinguishable.
Dart chooses to give different double values different toString
results. So does, e.g., Java and JavaScript, but not C# and Python.
That's not to say that we can't have a rounding operation which rounds to a specific number of decimal digits (and then to the nearest representable double value).
@lrhn
That's not to say that we can't have a rounding operation which rounds to a specific number of decimal digits (and then to the nearest representable double value).
That's exactly what the original question is asking for...not to mention my comment as well.
Yes. That was an attempt to get back on track, and not discuss toString
:smile:
@lrhn It was never off track. You're the only who's mentioned toString
so far in fact. I think you're confused, everyone is talking about toStringAsFixed
because that gives decimal precision. We want the equivalent but for doubles.
I have a solution. It's kinda of a hack.
Multiply the double by a 100 (Increase if you want more precision) then use roundToDouble()
and then divide by 100 again.
double num = 0.59841;
double roundedNum = (( num *100).roundToDouble())/100;
@YoussefLasheen The original post stated that he wanted to specify the amount of decimal precision with a simple integer (1, 2, 3 etc). I already made this kind of formula like yours as a workaround too but I think also the point also is we want it built into Dart like toStringAsFixed
Taking @YoussefLasheen's idea a bit further, we can write our own extension methods. Keep in mind that working with doubles is not precise and can give unexpected results. Also, a proper implementation for this should also assert
that the number of digits is not higher than a certain number, so pow(10, digits) does not overflow.
import 'dart:math';
extension DoubleRounding on double {
/// Floors to given number of digits
///
/// 10.2468.floorDigits(1) -> 10.2
/// 10.2468.floorDigits(2) -> 10.24
/// 10.2468.floorDigits(3) -> 10.246
///
/// Might give unexpected results due to precision loss: 10.2.floorDigits(5) -> 10.199999999999999
double floorDigits(int digits) {
if (digits == 0) {
return this.floorToDouble();
} else {
final divideBy = pow(10, digits);
return ((this * divideBy).floorToDouble() / divideBy);
}
}
double roundDigits(int digits) {
if (digits == 0) {
return this.roundToDouble();
} else {
final divideBy = pow(10, digits);
return ((this * divideBy).roundToDouble() / divideBy);
}
}
double ceilDigits(int digits) {
if (digits == 0) {
return this.ceilToDouble();
} else {
final divideBy = pow(10, digits);
return ((this * divideBy).ceilToDouble() / divideBy);
}
}
}
Working with doubles while requiring precision is never easy. They are designed to be lossful.
Multiplying by pow(10, x)
can lose precision (it increases the number of bits required by roughly 2 per x, which might push a number past the 53 available bits of precision). If you only care about .roundDigits(n)
for small n
s and small numbers, it's going to be fine. If you end up with something already using 48 bits above the decimal point, asking for 3 digits of precision might give a slightly wrong result.
Example:
var x = (pow(2,43) - 1) + .0625;
print(x); // 8796093022207.0625
print(x.roundDigits(5)); // 8796093022207.063
@lrhn I agree with everything you say. The thread itself asks for something that doubles are not able to do properly. Even so, there might be cases where the proposed solution is useful.
The functionality can definitely exist. We have the code which does toString
and toStringAsFixed
, and it can certainly be adapted to generate a new double which is equal to what toStringAsFixed
would have output. I'm not sure it's going to be particularly more efficient than actually doing toStringAsFixed
, but obviously it can avoid the string allocation.
It's still somewhat worrying to have a function documented as rounding a double to a number of decimal points when most decimal fractions cannot be represented precisely as a double.
We want to avoid doing something which gives a wrong result in some situations. Either you can trust the function, or you can't. It's not enough that it works for small numbers. (Obviously, if we know that it works for small numbers below some threshold, we can use it for small numbers and fall back on toStringAsFixed
+parse
for large numbers).
Tested @andreidiaconu ' s methods for a workaround and it works for me.
IMO, people who care about decimal representations probably should consider using something like package:decimal
instead of double
s anyway, and then rounding to a specified number of digits is fairly straightforward. (package:decimal
also internally uses BigInt
and should avoid problems with overflow.)
This issue was originally filed by adrian.avil...@gmail.com
If I have the number 12.346 and I need to round it to only two decimals is not possible, all I have is 12.346.round(), and the result is 12.0. It would be ideal to do 12.346.round(2) so the result can be 12.35.
References:
In C# there is Math.Round(double, decimals)
In Pascal there is RoundTo(double, decimals).