How do you round a double in Dart to a given degree of precision AFTER the decimal point?
Asked Answered
D

29

295

Given a double, I want to round it to a given number of points of precision after the decimal point, similar to PHP's round() function.

The closest thing I can find in the Dart docs is double.toStringAsPrecision(), but this is not quite what I need because it includes the digits before the decimal point in the total points of precision.

For example, using toStringAsPrecision(3):

0.123456789 rounds to 0.123  
9.123456789 rounds to 9.12  
98.123456789 rounds to 98.1  
987.123456789 rounds to 987  
9876.123456789 rounds to 9.88e+3

As the magnitude of the number increases, I correspondingly lose precision after the decimal place.

Disenthrall answered 9/2, 2015 at 21:0 Comment(0)
H
461

See the docs for num.toStringAsFixed().

toStringAsFixed(int fractionDigits) → String

A decimal-point string-representation of this number.

Converts this to a double before computing the string representation.

  • If the absolute value of this is greater or equal to 10^21 then this methods returns an exponential representation computed by this.toStringAsExponential().

    Examples:

    1000000000000000000000.toStringAsExponential(3); // 1.000e+21
    
  • Otherwise the result is the closest string representation with exactly fractionDigits digits after the decimal point. If fractionDigits equals 0 then the decimal point is omitted.

    The parameter fractionDigits must be an integer satisfying: 0 <= fractionDigits <= 20.

    Examples:

    1.toStringAsFixed(3);  // 1.000
    (4321.12345678).toStringAsFixed(3);  // 4321.123
    (4321.12345678).toStringAsFixed(5);  // 4321.12346
    123456789012345678901.toStringAsFixed(3);  // 123456789012345683968.000
    1000000000000000000000.toStringAsFixed(3); // 1e+21
    5.25.toStringAsFixed(0); // 5
    
Historian answered 9/2, 2015 at 21:11 Comment(4)
I argue is toStringAsFixed() the rounding method to use due to its inconsistency. For example, try 5.550.toStringAsFixed(1)Edgewise
Also watch out when rounding to 0 places as the expression returns an int instead of a double in that one case e.g. num.parse(50.123.toStringAsFixed(0)) is int - this can be fixed by adding .toDouble() to the end of it.Grout
@Edgewise Given that a double that attempts to store 5.55 is actually 5.5499999999999998... then toStringAsFixed(1) returns the correct answer (5.5).Voluntary
Is there a good way to do this, but then also have it show zero decimals if the trailing decimals are all zeros? Ex: 12.000 would show as 12 but 12.011 would still show as 12.011?Bulletin
P
135

num.toStringAsFixed() rounds. This one turns you num (n) into a string with the number of decimals you want (2), and then parses it back to your num in one sweet line of code:

n = num.parse(n.toStringAsFixed(2));
Pocket answered 25/8, 2015 at 13:22 Comment(8)
That's the quickest way to do it. But it's dumb that dart doesn't have a direct way to do thisNahamas
Extension method: extension NumberRounding on num { num toPrecision(int precision) { return num.parse((this).toStringAsFixed(precision)); } }Toluate
@Nahamas Dart does have a direct way: toStringAsFixed. It does not have a direct way of rounding a double to a specified number of decimal digits and keeping the result as a double because that's a dumb thing to do. The decimal value you want very likely will not be exactly representable as a double.Voluntary
I think that this answer should clarify that what this code actually does is to return the nearest double to the rounded value.Voluntary
Had run some quick measurements. toString and parsing is roughly 50x slower than the math solution with POW for 10 million iterations. 25x slower for 1 million iterations and 10x slower for 100 000 iterations. In case someone wondered, like me.Chetnik
@Voluntary Rounding a double to a specified number of decimal digits and keeping the result as a double isn't necessarily dumb. For example if you are writing the result to a JSON file containing thousands of such numbers, this can have a significant impact on the size of the file produced.Barbiturism
@JamesAllen You won't get any savings if you want to store 0.1 (which isn't representable in binary floating-point) but your JSON implementation writes 0.100000000000000005551115123125782702118158340454101562. Rounding a binary number to a number of decimal digits is a nonsensical operation. What you want is to round or truncate the serialized string representation, not to round and store the value as a double.Voluntary
@JamesAllen Now, it might happen that your JSON implementation prettifies the output by writing the shortest decimal number whose binary representation matches your double, but unless that's guaranteed by documentation, you shouldn't rely on that.Voluntary
B
106

Direct way:

double d = 2.3456789;
String inString = d.toStringAsFixed(2); // '2.35'
double inDouble = double.parse(inString); // 2.35 

Using an extension:

extension Ex on double {
  double toPrecision(int n) => double.parse(toStringAsFixed(n));
}

Usage:

void main() {
  double d = 2.3456789;
  double d1 = d.toPrecision(1); // 2.3
  double d2 = d.toPrecision(2); // 2.35
  double d3 = d.toPrecision(3); // 2.345
}
Braid answered 28/9, 2020 at 7:19 Comment(5)
I dont understand how this works. Why does double.parse returns the double since we don't pass a reference to it? (I'm thinking of something like this.parse(toStringAsFixed(n));)Undoubted
@Undoubted double.parse is a static method on a double type.Voluntary
I think that this answer should clarify that what this code actually does is to return the nearest double to the rounded value.Voluntary
this will not work for 2.30Directory
@AdityaPatil What will not work? How do you want round 2.30?Braid
B
75

Above solutions do not appropriately round numbers. I use:

double dp(double val, int places){ 
   num mod = pow(10.0, places); 
   return ((val * mod).round().toDouble() / mod); 
}
Balm answered 27/11, 2018 at 13:5 Comment(7)
Confirmed, this rounds dp(5.550, 1) correctly to 5.6 - unlike the most popular answer which gives a slightly incorrect answer of 5.5 as @Edgewise pointed out.Grout
Does not work for dp(73.4750, 2) It returns 73.47 instead of 73.48 calculatorsoup.com/calculators/math/… check out this linkRubbish
@DeepShah I believe this answer -> [ https://mcmap.net/q/94768/-how-do-you-round-a-double-in-dart-to-a-given-degree-of-precision-after-the-decimal-point ] addresses your concern about the " number 5 rounding rule " issue you identified here above.Scorekeeper
This is inherently problematic because rounding IEEE-754 floating-point numbers (which are binary) to decimal precision is nonsensical; many fractional decimal numbers can't be represented in binary. Also see Is floating point math broken?.Voluntary
can you please convert 4436.605 to 4436.61 (with 2 places)Punke
@DeepShah To prevent floating-point errors use this package and round with: Decimal.parse(this.toString()).round(scale: places).toDouble(). Rounding is now correct.Abstract
@DeepShah, Paul - see my comment here [ https://mcmap.net/q/94768/-how-do-you-round-a-double-in-dart-to-a-given-degree-of-precision-after-the-decimal-point ] ...Scorekeeper
T
37
var price = 99.012334554;
price = price.toStringAsFixed(2);
print(price); // 99.01

That is the ref of dart. ref: https://api.dartlang.org/stable/2.3.0/dart-core/num/toStringAsFixed.html

Tc answered 4/5, 2019 at 11:8 Comment(1)
This answer does not add anything to Greg Lowe's much earlier answer.Voluntary
G
32
void main() {
  int decimals = 2;
  int fac = pow(10, decimals);
  double d = 1.234567889;
  d = (d * fac).round() / fac;
  print("d: $d");
}

Prints: 1.23

Gruchot answered 9/2, 2015 at 21:6 Comment(3)
This is the best current way to do this. Rounding to a precision like 0.01 that isn't itself a double, isn't trivial. The result may not even be representable as a double at all. I highly recommend using integers for anything where decimal precision is important (like, say, money).Katabolism
Also worth pointing out that if compiling to js, then Dart integers will also start to lose precision and you'll need to use a package like: pub.dartlang.org/packages/bignumHistorian
This method feels like the current best and most elegant way to do this.Nothing
U
27

I used the toStringAsFixed() method, to round a number to specific numbers after the decimal point EX:

double num = 22.48132906

and when I rounded it to two numbers like this:

print(num.toStringAsFixed(2)) ;

It printed 22.48

and when I rounded to one number, it printed 22.5

Unofficial answered 24/12, 2019 at 18:55 Comment(1)
This answer does not add anything to Greg Lowe's much earlier answer.Voluntary
S
22

The modified answer of @andyw using Dart Extension methods:

extension Precision on double {
    double toPrecision(int fractionDigits) {
        double mod = pow(10, fractionDigits.toDouble());
        return ((this * mod).round().toDouble() / mod);
    }
}

Usage:

var latitude = 1.123456;
var latitudeWithFixedPrecision = latitude.toPrecision(3); // Outputs: 1.123
Shovelboard answered 29/12, 2019 at 18:9 Comment(0)
M
17

you can simply multiple the value in 100 and then round it and then divide it again into 100.

(number * 100).round() / 100.0;
Mercurate answered 23/7, 2021 at 9:29 Comment(1)
This is actually brilliant. I did not think of this. Thanks.Leavetaking
S
14

To round a double in Dart to a given degree of precision AFTER the decimal point, you can use built-in solution in dart toStringAsFixed() method, but you have to convert it back to double

void main() {
  double step1 = 1/3;  
  print(step1); // 0.3333333333333333
  
  String step2 = step1.toStringAsFixed(2); 
  print(step2); // 0.33 
  
  double step3 = double.parse(step2);
  print(step3); // 0.33
}

Staminody answered 31/5, 2020 at 12:47 Comment(1)
This answer does not add anything to Yngvar Natland's much earlier answer. Also, I think that this answer should clarify that what this code actually does is to return the nearest double to the rounded value.Voluntary
P
10
double value = 2.8032739273;
String formattedValue = value.toStringAsFixed(3);
Pinky answered 29/1, 2021 at 10:38 Comment(2)
please add more explanation to your answer for benefit of other usersUncivil
This is not syntactically correct, toStringAsFixed takes a parameterVelours
W
8

You can use toStringAsFixed in order to display the limited digits after decimal points. toStringAsFixed returns a decimal-point string-representation. toStringAsFixed accepts an argument called fraction Digits which is how many digits after decimal we want to display. Here is how to use it.

double pi = 3.1415926;
const val = pi.toStringAsFixed(2); // 3.14
Wennerholn answered 4/8, 2020 at 6:42 Comment(0)
T
6

You can create a reusable function that accept numberOfDecimal you want to format & utilizing toStringAsFixed() method to format the number and convert it back to double.

FYI, toStringAsFixed method does not round up number that ends with 5 (eg: toStringAsFixed round off 2.275 to 2.27 instead of 2.28). This is the default behaviour of dart toStringAsFixed method (similar to Javascript toFixed)

As a workaround, we can add 1 to the existing number after the last decimal number (eg: Add 0.0001 to 2.275 become 2.2751 & 2.2751 will round off correctly to 2.28)

double roundOffToXDecimal(double number, {int numberOfDecimal = 2}) {
  // To prevent number that ends with 5 not round up correctly in Dart (eg: 2.275 round off to 2.27 instead of 2.28)
  String numbersAfterDecimal = number.toString().split('.')[1];
  if (numbersAfterDecimal != '0') {
    int existingNumberOfDecimal = numbersAfterDecimal.length;
    double incrementValue = 1 / (10 * pow(10, existingNumberOfDecimal));
    if (number < 0) {
       number -= incrementValue;
    } else {
       number += incrementValue;
    }
  }

  return double.parse(number.toStringAsFixed(numberOfDecimal));
}

// Example of usage:
var price = roundOffToXDecimal(2.275, numberOfDecimal: 2)
print(price); // 2.28
Timmy answered 12/5, 2021 at 3:49 Comment(5)
Well done -> your solution works perfectly ( rounds + adheres to rounding rule ) and your code is way leaner than my solution. So I abandon my solution for yours - thanks for the effort man. [ two thumbs up ]Scorekeeper
.toStringAsFixed() does round up numbers that end with a 5; 0.5.toStringAsFixed(0) is '1', 0.25.toStringAsFixed(1) is '0.3', and 0.125.toStringAsFixed(2) is '0.13'. As explained by the thread this answer links to, the reason why that doesn't happen in general is because doubles are inherently imprecise when representing decimal numbers. For example, 2.275 as a double is actually 2.274999999999999911182158029987476766109466552734375.Voluntary
roundOffToXDecimal(8.5) to 8.51Italic
@Voluntary Yes, toStringAsFixed() does round as you indicated, but it certainly does not adhere to the rounding rule which states when rounding a number ending with a 5 round up if the result is an even number, but don't round if result is an odd number. So let's just agree on that and it will prevent us from going in an endless circle on this issue. I think Luke's solution does a good job in getting us to where we want to be. :)Scorekeeper
@Scorekeeper Your rule is one of several possible rounding rules. And you cannot guarantee that this answer always rounds according to that rule. As I stated, 2.275 as a double is actually 2.274999999999999911182158029987476766109466552734375, so that means that this code is rounding 2.274999999999999911182158029987476766109466552734375 up even though it's less than 2.275. (And again, when it gets rounded up, it's not actually rounded to 2.28, which isn't exactly representable as a double. It's rounded to 2.279999999999999804600747665972448885440826416015625.)Voluntary
Z
6

I made this extension on double

import 'dart:math';

extension DoubleExtension on double {

  /// rounds the double to a specific decimal place
  double roundedPrecision(int places) {
    double mod = pow(10.0, places) as double;
    return ((this * mod).round().toDouble() / mod);
  }

  /// good for string output because it can remove trailing zeros
  /// and sometimes periods. Or optionally display the exact number of trailing
  /// zeros
  String roundedPrecisionToString(
    int places, {
    bool trailingZeros = false,
  }) {
    double mod = pow(10.0, places) as double;
    double round = ((this * mod).round().toDouble() / mod);
    String doubleToString =
        trailingZeros ? round.toStringAsFixed(places) : round.toString();
    if (!trailingZeros) {
      RegExp trailingZeros = RegExp(r'^[0-9]+.0+$');
      if (trailingZeros.hasMatch(doubleToString)) {
        doubleToString = doubleToString.split('.')[0];
      }
    }
    return doubleToString;
  }

  String toStringNoTrailingZeros() {
    String doubleToString = toString();
    RegExp trailingZeros = RegExp(r'^[0-9]+.0+$');
    if (trailingZeros.hasMatch(doubleToString)) {
      doubleToString = doubleToString.split('.')[0];
    }
    return doubleToString;
  }
}

Here are the passing tests.

import 'package:flutter_test/flutter_test.dart';
import 'package:project_name/utils/double_extension.dart';

void main() {
  group("rounded precision", () {
    test("rounding to 0 place results in an int", () {
      double num = 5.1234;
      double num2 = 5.8234;
      expect(num.roundedPrecision(0), 5);
      expect(num2.roundedPrecision(0), 6);
    });
    test("rounding to 1 place rounds correctly to 1 place", () {
      double num = 5.12;
      double num2 = 5.15;
      expect(num.roundedPrecision(1), 5.1);
      expect(num2.roundedPrecision(1), 5.2);
    });
    test(
        "rounding a number to a precision that is more accurate than the origional",
        () {
      double num = 5;
      expect(num.roundedPrecision(5), 5);
    });
  });

  group("rounded precision returns the correct string", () {
    test("rounding to 0 place results in an int", () {
      double num = 5.1234;
      double num2 = 5.8234;
      expect(num.roundedPrecisionToString(0), "5");
      expect(num2.roundedPrecisionToString(0), "6");
    });
    test("rounding to 1 place rounds correct", () {
      double num = 5.12;
      double num2 = 5.15;
      expect(num.roundedPrecisionToString(1), "5.1");
      expect(num2.roundedPrecisionToString(1), "5.2");
    });
    test("rounding to 2 places rounds correct", () {
      double num = 5.123;
      double num2 = 5.156;
      expect(num.roundedPrecisionToString(2), "5.12");
      expect(num2.roundedPrecisionToString(2), "5.16");
    });
    test("cut off all trailing zeros (and periods)", () {
      double num = 5;
      double num2 = 5.03000;
      expect(num.roundedPrecisionToString(5), "5");
      expect(num2.roundedPrecisionToString(5), "5.03");
    });
  });
}
Zorn answered 24/9, 2021 at 19:57 Comment(0)
J
5

Above solutions do not work for all cases. What worked for my problem was this solution that will round your number (0.5 to 1 or 0.49 to 0) and leave it without any decimals:

Input: 12.67

double myDouble = 12.67;
var myRoundedNumber; // Note the 'var' datatype

// Here I used 1 decimal. You can use another value in toStringAsFixed(x)
myRoundedNumber = double.parse((myDouble).toStringAsFixed(1));
myRoundedNumber = myRoundedNumber.round();

print(myRoundedNumber);

Output: 13

This link has other solutions too

Juliusjullundur answered 15/6, 2020 at 17:38 Comment(0)
B
3

If you don't want any decimals when the resulting decimals are all zeroes, something like this would work:

String fixedDecimals(double d, int decimals, {bool removeZeroDecimals = true}){
  double mod = pow(10.0, decimals);
  double result = ((d * mod).round().toDouble() / mod);
  if( removeZeroDecimals && result - (result.truncate()) == 0.0 ) decimals = 0;
  return result.toStringAsFixed(decimals);
}

This will simply output 9 instead of 9.00 if the input is 9.004 and you want 2 decimals.

Bushelman answered 10/12, 2020 at 9:47 Comment(1)
I don't know why you guys are all giving truncating solutions when the questions clearly states -> How do you round a double in Dart to a given degree of precision ...Scorekeeper
V
3

Rounding a double, an IEEE-754 binary floating-point number, to a specific number of decimal digits is inherently problematic if you want a double result.

In the same way that fractions such as 1/3 can't be exactly represented with a finite number of decimal digits, many (well, infinitely many) decimal numbers can't be represented with a finite number of binary digits. For example, the decimal number 0.1 cannot be exactly represented in binary. While you could try to round 0.09999 to 0.1, as a double it would actually be "rounded" to 0.1000000000000000055511151231257827021181583404541015625. Most of the other answers that claim to round doubles with decimal precision actually return the nearest representable double. When you display those values to users by converting them to Strings, you might see more digits than you expect.

What you can do is to make the string representation look like a nice, rounded number when you ultimately show it to users, and that's what double.toStringAsFixed() does. That's also why when you print 0.100000000..., you might see 0.1 if the implementation is trying to pretty-print user-friendly values. However, don't be fooled: the double value would never actually be 0.1 exactly, and if you do repeated arithmetic with such inexact values, you can accumulate error (for example: 0.1 + 0.2).

Note that all of the above is fundamental to how binary floating-point numbers work and is not specific to Dart. Also see:

Bottom line: If you care about decimal precision, do NOT use binary floating-point types. This is particularly important if you're dealing with money.

You instead should use:

  • Integers. For example, if you are dealing with currency, instead of using double dollars = 1.23;, use int cents = 123;. Your calculations then always will be exact, and you can convert to the desired units only when displaying them to the user (and likewise can convert in the opposite direction when reading input from the user).
  • A type designed to represent decimal numbers with arbitrary precision. For example, package:decimal provides a Decimal type. With such a type, some of the other answers (such as multiplying by 100, rounding, and then dividing by 100) then would be appropriate. (But really you should use Decimal.round directly.)
Voluntary answered 30/8, 2021 at 7:51 Comment(4)
To round accurately with this package use: Decimal.parse(this.toString()).round(scale: places).toDouble()Abstract
@Abstract You should get rid of the toDouble(). That defeats the point of using package:decimal.Voluntary
There might be cases where a double return is questioned, so I thought I should add it. Also: this rounds a double correctly (possibly only once). But yes, If you would need to do calculations with doubles/decimals you shouldn't do that obviously.Abstract
@Abstract "Rounds a double correctly" is a nonsensical statement since you cannot exactly round a double in terms of base-10 digits. Fundamentally there are base-10 numbers that cannot be exactly represented as doubles. No "rounding" can avoid that.Voluntary
B
2

Just write this extension on double

extension Round on double {
  double roundToPrecision(int n) {
    int fac = pow(10, n).toInt();
    return (this * fac).round() / fac;
  }
}
Brainbrainard answered 30/1, 2021 at 19:39 Comment(2)
But you obviously need to do something about the int fac. pow() returns a num, not an int. So, toInt().Bilious
solved the issueBrainbrainard
I
2

I think the accepted answer is not the perfect solution because it converts to string.

If you don't wanna convert to string and back to a double use double.toPrecision(decimalNumber) from GetX package.

If you don't wanna use GetX just for this (I highly recommend GetX, it will change your life with flutter) you can copy and paste this.

Remeber to import the file when you wanna use the extention.

import 'dart:math';

extension Precision on double {
  double toPrecision(int fractionDigits) {
    var mod = pow(10, fractionDigits.toDouble()).toDouble();
    return ((this * mod).round().toDouble() / mod);
  }
}
Indetermination answered 5/2, 2021 at 17:11 Comment(0)
E
2

if use dynamic type of data. You can use it.

 typeDecimal(data) => num.parse(data.toString()).toStringAsFixed(2);
Epi answered 22/6, 2021 at 14:46 Comment(0)
H
2

also if you want to round the double value inside the Text.

Text('${carpetprice.toStringAsFixed(3)}',),

Houle answered 14/8, 2021 at 8:24 Comment(0)
F
1

Never thought this was so complex in Dart but this is my solution:

double truncateDouble(double val, int decimals) {
    String valString = val.toString();
    int dotIndex = valString.indexOf('.');

    // not enough decimals
    int totalDecimals = valString.length - dotIndex - 1;
    if (totalDecimals < decimals) {
      decimals = totalDecimals;
    }

    valString = valString.substring(0, dotIndex + decimals + 1);

    return double.parse(valString);
  }

var val = truncateDouble(44.999, 2);
Fourchette answered 7/12, 2022 at 0:4 Comment(3)
As with almost all of the other answers, this answer should clarify that what this code actually does is to return the nearest double to the truncated value, so when you convert it back to a String, you might end up with many more digits than you expect. It is inherently impossible to exactly represent most fractional decimal numbers in binary.Voluntary
@Fourchette - This solution also fails the basic rounding rule -> truncateDouble(73.4750, 2) returns 73.47 and not the correct value of 73.48 . @Voluntary - alright then ... give us your solution (or atleast try to formulate a solution) instead of just shooting down others' attempts at trying to find a work-around to this "well-known" rounding problem.Scorekeeper
@Scorekeeper I've already provided my own answer. Furthermore, I'd hardly call suggesting a clarification to be "shooting down" an answer.Voluntary
S
0

ABANDONED !!! -> Please see @luke77 's code - it correctly solves this problem with much leaner code.

This DART rounding problem has been a long time coming (@LucasMeadows), since it's clear that it has not been adequately solved (as indicated by @DeepShah's observation) until now.

The well-known rounding rule (the unsolved problem):

" Rounding numbers that end with the number 5: round up if the result is an even number; round down if the result is an odd number. "

So here is the DART code solution:

double roundAccurately(double numToRound, int decimals) {

  // Step 1 - Prime IMPORTANT Function Parameters ...
  int iCutIndex = 0;
  String sDeciClipdNTR = "";
  num nMod = pow(10.0, decimals);
  String sNTR = numToRound.toString();
  int iLastDigitNTR = 0, i2ndLastDigitNTR = 0;
  debugPrint("Round => $numToRound to $decimals Decimal ${(decimals == 1) ? "Place" : "Places"} !!");   // Deactivate this 'print()' line in production code !!

  // Step 2 - Calculate Decimal Cut Index (i.e. string cut length) ...
  int iDeciPlaces = (decimals + 2);
  if (sNTR.contains('.')) {
    iCutIndex = sNTR.indexOf('.') + iDeciPlaces;
  } else {
    sNTR = sNTR + '.';
    iCutIndex = sNTR.indexOf('.') + iDeciPlaces;
  }

  // Step 3 - Cut input double to length of requested Decimal Places ...
  if (iCutIndex > sNTR.length) {                    // Check that decimal cutting is possible ...
    sNTR = sNTR + ("0" * iDeciPlaces);              // ... and fix (lengthen) the input double if it is too short.
    sDeciClipdNTR = sNTR.substring(0, iCutIndex);   // ... then cut string at indicated 'iCutIndex' !!
  } else {
    sDeciClipdNTR = sNTR.substring(0, iCutIndex);   // Cut string at indicated 'iCutIndex' !!
  }

  // Step 4 - Extract the Last and 2nd Last digits of the cut input double.
  int iLenSDCNTR = sDeciClipdNTR.length;
  iLastDigitNTR = int.parse(sDeciClipdNTR.substring(iLenSDCNTR - 1));   // Extract the last digit !!
  (decimals == 0)
    ? i2ndLastDigitNTR = int.parse(sDeciClipdNTR.substring(iLenSDCNTR - 3, iLenSDCNTR - 2))
    : i2ndLastDigitNTR = int.parse(sDeciClipdNTR.substring(iLenSDCNTR - 2, iLenSDCNTR - 1));

  // Step 5 - Execute the FINAL (Accurate) Rounding Process on the cut input double.
  double dAccuRound = 0;
  if (iLastDigitNTR == 5 && ((i2ndLastDigitNTR + 1) % 2 != 0)) {
    dAccuRound = double.parse(sDeciClipdNTR.substring(0, iLenSDCNTR - 1));
  } else {
    if (iLastDigitNTR < 5) {
      dAccuRound = double.parse(sDeciClipdNTR.substring(0, iLenSDCNTR - 1));
    } else {
      if (decimals == 0) {
        sDeciClipdNTR = sNTR.substring(0, iCutIndex - 2);
        dAccuRound = double.parse(sDeciClipdNTR) + 1;   // Finally - Round UP !!
      } else {
        double dModUnit = 1 / nMod;
        sDeciClipdNTR = sNTR.substring(0, iCutIndex - 1);
        dAccuRound = double.parse(sDeciClipdNTR) + dModUnit;   // Finally - Round UP !!
      }
    }
  }

  // Step 6 - Run final QUALITY CHECK !!
  double dResFin = double.parse(dAccuRound.toStringAsFixed(decimals));

  // Step 7 - Return result to function call ...
  debugPrint("Result (AccuRound) => $dResFin !!");   // Deactivate this 'print()' line in production code !!
  return dResFin;
}

It's a completely manual approach (and probably a bit of an overkill), but it works. Please test it (to exhaustion) and let me know if I've missed the mark.

ABANDONED !!! -> Please see @luke47 's code - it correctly solves this problem with much leaner code.

Scorekeeper answered 28/3, 2021 at 11:5 Comment(6)
Updated function (above): roundAccurately(73.4750, 2) returns 73.48 !! @deep-shahScorekeeper
This is extremely complicated. Have a look at this answer and only use: Decimal.parse(this.toString()).round(scale: places).toDouble()Abstract
@Abstract - so you want replace a standalone function with 63 lines of code with a 3rd Party Library [ github.com/a14n/dart-decimal/blob/master/lib/decimal.dart ] that has over 300 lines of code? Hmm... Also, have you tried quick trials with this library in Dartpad.dev? ( good luck with that !! ) ...but to each his own, I guess.Scorekeeper
there is no other solution than re-writing Dart from scratch??Fourchette
Actually I posted another solutionFourchette
It is misleading to call this "a Dart problem". This is simply how binary floating-point numbers work. Binary floating-point numbers cannot exactly represent fractional decimal numbers in any language.Voluntary
B
0

If you want use special rounding. You can try this function (rounding).

void main(List<String> arguments) {
list.map((e) {
 log('list1');
 rounding(e, 0.05);
 rounding(e, 0.1);
 rounding(e, 0.2);
 rounding(e, 0.25);
 rounding(e, 0.5);
 rounding(e, 1);
 rounding(e, 10);
}).toList();
list2.map((e) {
 log('list2');
 rounding(e, 0.05);
 rounding(e, 0.1);
 rounding(e, 0.2);
 rounding(e, 0.25);
 rounding(e, 0.5);
 rounding(e, 1);
 rounding(e, 10);
}).toList();
}

const list = [1.11, 1.22, 1.33, 1.44, 1.55, 1.66, 1.77, 1.88, 1.99];

const list2 = [2.19, 3.28, 4.37, 5.46, 6.55, 7.64, 8.73, 9.82, 10.91];

void rounding(double price, double count) {
log('-----------------------');
log('price: $price, count: $count');
double _priceRemainder = price % count;
double _someDiff = count / _priceRemainder;
log('_price: ${_priceRemainder.toStringAsFixed(2)}');
log('_pricePlus: ${_someDiff.toStringAsFixed(2)}');
if (_someDiff.toStringAsFixed(2) == '1.00') {
 log('_someDiff = 1');
} else if (_someDiff > 1 && _someDiff <= 2 ||
   _someDiff.toStringAsFixed(2) == '2.00') {
 log('_someDiff > 1 && _someDiff <= 2 || _someDiff.toStringAsFixed(2) == 2.00');
 log('ceilToDouble: $price: ${(price + (count - _priceRemainder)).toStringAsFixed(2)}');
 log('floorToDouble: $price: ${(price - _priceRemainder).toStringAsFixed(2)}');
 log('roundToDouble: $price: ${(price + (count - _priceRemainder)).toStringAsFixed(2)}');
} else if (_someDiff > 2) {
 log('_someDiff > 2');
 log('ceilToDouble: $price: ${(price + (count - _priceRemainder)).toStringAsFixed(2)}');
 log('floorToDouble: $price: ${(price - _priceRemainder).toStringAsFixed(2)}');
 log('roundToDouble: $price: ${(price - _priceRemainder).toStringAsFixed(2)}');
}
log('-----------------------');
}

Debug console:


[log] price: 10.91, count: 0.05
[log] _price: 0.01
[log] _pricePlus: 5.00
[log] _someDiff > 2
[log] ceilToDouble: 10.91: 10.95
[log] floorToDouble: 10.91: 10.90
[log] roundToDouble: 10.91: 10.90
2
[log] -----------------------
[log] price: 10.91, count: 0.1
[log] _price: 0.01
[log] _pricePlus: 10.00
[log] _someDiff > 2
[log] ceilToDouble: 10.91: 11.00
[log] floorToDouble: 10.91: 10.90
[log] roundToDouble: 10.91: 10.90
2
[log] -----------------------
[log] price: 10.91, count: 0.2
[log] _price: 0.11
[log] _pricePlus: 1.82
[log] _someDiff > 1 && _someDiff <= 2 || _someDiff.toStringAsFixed(2) == 2.00
[log] ceilToDouble: 10.91: 11.00
[log] floorToDouble: 10.91: 10.80
[log] roundToDouble: 10.91: 11.00
2
[log] -----------------------
[log] price: 10.91, count: 0.25
[log] _price: 0.16
[log] _pricePlus: 1.56
[log] _someDiff > 1 && _someDiff <= 2 || _someDiff.toStringAsFixed(2) == 2.00
[log] ceilToDouble: 10.91: 11.00
[log] floorToDouble: 10.91: 10.75
[log] roundToDouble: 10.91: 11.00
2
[log] -----------------------
[log] price: 10.91, count: 0.5
[log] _price: 0.41
[log] _pricePlus: 1.22
[log] _someDiff > 1 && _someDiff <= 2 || _someDiff.toStringAsFixed(2) == 2.00
[log] ceilToDouble: 10.91: 11.00
[log] floorToDouble: 10.91: 10.50
[log] roundToDouble: 10.91: 11.00
2
[log] -----------------------
[log] price: 10.91, count: 1.0
[log] _price: 0.91
[log] _pricePlus: 1.10
[log] _someDiff > 1 && _someDiff <= 2 || _someDiff.toStringAsFixed(2) == 2.00
[log] ceilToDouble: 10.91: 11.00
[log] floorToDouble: 10.91: 10.00
[log] roundToDouble: 10.91: 11.00
2
[log] -----------------------
[log] price: 10.91, count: 10.0
[log] _price: 0.91
[log] _pricePlus: 10.99
[log] _someDiff > 2
[log] ceilToDouble: 10.91: 20.00
[log] floorToDouble: 10.91: 10.00
[log] roundToDouble: 10.91: 10.00
Bailly answered 18/4, 2022 at 12:34 Comment(0)
H
0

If you need proper rounding (up when first digit is 5) and you want to have trailing 0's you can use this method:

import 'dart:math';

String customRound(double val, int places) {
  num mod = pow(10.0, places);
  return ((val * mod).round().toDouble() / mod).toStringAsFixed(places);
}

customRound(2.345) // -> 2.35
customRound(2.500) // -> 2.50
Hengist answered 24/6, 2022 at 10:10 Comment(0)
B
0
double roundOffToXDecimal(num number, {int precision = 2}) {
  var precisionWithPow10 = pow(10, precision);
  return (number * precisionWithPow10).round() / precisionWithPow10;
Bond answered 6/10, 2023 at 14:48 Comment(1)
Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?Tonsillectomy
C
-1

This function you can call to get degree of precision in dark(flutter). double eval -> double that want to convert int i -> number of decimal point to return.

double doubleToPrecision(double eval, int i) {
double step1 = eval;//1/3
print(step1); // 0.3333333333333333

String step2 = step1.toStringAsFixed(2);
print(step2); // 0.33

double step3 = double.parse(step2);
print(step3); // 0.33
eval = step3;
return eval; }
Cripple answered 6/12, 2021 at 19:44 Comment(0)
C
-2

I prever converting my like this => `

num.tryParse("23.123456789")!.toDouble().roundToDouble()

`

Caravansary answered 4/12, 2021 at 23:17 Comment(0)
K
-5

This works pretty well

var price=99.012334554
price = price.roundTodouble();
print(price); // 99.01
Knee answered 8/5, 2020 at 7:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.