Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Round for doubles should take the number of decimals as argument... #8575

Open
DartBot opened this issue Feb 16, 2013 · 20 comments
Open

Round for doubles should take the number of decimals as argument... #8575

DartBot opened this issue Feb 16, 2013 · 20 comments
Labels
area-library core-l P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug

Comments

@DartBot
Copy link

DartBot commented Feb 16, 2013

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).

@lrhn
Copy link
Member

lrhn commented Feb 18, 2013

Removed Type-Defect label.
Added Type-Enhancement, Area-Library, Triaged labels.

@DartBot
Copy link
Author

DartBot commented Nov 15, 2014

This comment was originally written by adrian.avila.mtz...@gmail.com


No plans for fixing this one yet?

Has been a while.

@eukreign
Copy link
Contributor

eukreign commented Nov 4, 2015

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.

@zoechi
Copy link
Contributor

zoechi commented Nov 4, 2015

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.

@eukreign
Copy link
Contributor

eukreign commented Nov 4, 2015

@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.

@zoechi
Copy link
Contributor

zoechi commented Nov 4, 2015

@eukreign I don't argue against that. I think that it just can't be integrated into num.round() because the return types differ.

@kevmoo kevmoo added P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug and removed triaged labels Feb 29, 2016
@lrhn lrhn added the core-m label Aug 11, 2017
@floitschG floitschG added core-l and removed core-m labels Aug 29, 2017
@AbdullahAlAsad
Copy link

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.

@hasen6
Copy link

hasen6 commented Oct 6, 2019

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.

@dart-lang dart-lang deleted a comment from shaxxx Oct 25, 2019
@lrhn
Copy link
Member

lrhn commented Oct 25, 2019

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).

@hasen6
Copy link

hasen6 commented Oct 25, 2019

@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.

@lrhn
Copy link
Member

lrhn commented Oct 25, 2019

Yes. That was an attempt to get back on track, and not discuss toString 😄

@hasen6
Copy link

hasen6 commented Oct 25, 2019

@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.

@YoussefLasheen
Copy link

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;

@hasen6
Copy link

hasen6 commented Nov 2, 2019

@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

@andreidiaconu
Copy link

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);
    }
  }
}

@lrhn
Copy link
Member

lrhn commented Oct 8, 2020

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 ns 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

@andreidiaconu
Copy link

@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.

@lrhn
Copy link
Member

lrhn commented Oct 12, 2020

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).

@mario-neb
Copy link

Tested @andreidiaconu ' s methods for a workaround and it works for me.

@jamesderlin
Copy link
Contributor

IMO, people who care about decimal representations probably should consider using something like package:decimal instead of doubles 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.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-library core-l P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests