Lucas Meadows
Lucas Meadows

Reputation: 3159

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

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.

Upvotes: 308

Views: 399601

Answers (30)

LabGecko
LabGecko

Reputation: 301

These functions handle scientific notation and normal number values using math instead of string manipulation where possible. Of course the functions can be used without the test groups in void main(), but if you prefer to test that these work:

  • Save the code below as util_math_test.dart in the test folder
  • In your IDE terminal run flutter test test/util_math_test.dart


    import 'dart:math';
    import 'package:flutter_test/flutter_test.dart';
    import 'package:gg_carousel/error_logger.dart';
    
    /// Count the number of decimal places in a number, with or without scientific notation
    int countDecimalPlaces(num number) {
      // If the number is already an integer, return 0
      if (number is int || number == number.toInt()) { return 0; }
    
      // Convert the number to a string
      String numString = number.toString();
    
      if (numString.contains('e') || numString.contains('E')) {
        // Handle scientific notation
        List parts = numString.split(RegExp(r'[eE]'));
        // logError('countDecimalPlaces parts $parts');
        String significantDigits = parts[0];
        int exponent = int.parse(parts[1]);
        // logError('countDecimalPlaces significantDigits $significantDigits, exponent $exponent');
    
        // Count decimal places in the significant digits
        int decimalPlaces = 0;
        if (significantDigits.contains('.')) {
          decimalPlaces = significantDigits.split('.')[1].length;
        }
    
        // Adjust for the exponent in scientific notation
        int effectiveDecimalPlaces = decimalPlaces - exponent;
        return max(0, effectiveDecimalPlaces);
      }
      else {
        // Handle regular decimal notation
        List parts = numString.split('.');
        if (parts.length > 1) {
          // Count decimal places only if not followed by `.0`
          return int.parse(parts[1]) == 0 ? 0 : parts[1].length;
        }
        return 0;
      }
    }
    
    /// Add two numbers with decimal precision
    double addNumPrecision(num a, num b) {
      int precisionA = countDecimalPlaces(a);
      int precisionB = countDecimalPlaces(b);
      int maxPrecision = max(precisionA, precisionB);
      num multiplier = pow(10, maxPrecision);
      num result = (a * multiplier).round() + (b * multiplier).round();
      return result / multiplier;
    }
    
    /// Rounds a number to a specified number of decimal places.
    double roundToDecimalPlaces(double number, int decimalPlaces) {
      double multiplier = pow(10, decimalPlaces).toDouble();
      return (number * multiplier).round() / multiplier;
    }
    
    void main() {
      group('countDecimalPlaces', () {
        test('counts decimal places for 1.234', () { expect(countDecimalPlaces(1.234), equals(3)); });
        test('counts decimal places for 0.00123', () { expect(countDecimalPlaces(0.00123), equals(5)); });
        test('counts decimal places for 123.0', () { expect(countDecimalPlaces(123.0), equals(0)); });
        test('counts decimal places for 123.456789', () { expect(countDecimalPlaces(123.456789), equals(6)); });
        test('counts decimal places for 123456.789012', () { expect(countDecimalPlaces(123456.789012), equals(6)); });
    
        test('failed handling scientific notation for 1.23e-5', () { expect(countDecimalPlaces(1.23e-5), equals(7)); });
        test('failed handling scientific notation for 123e3', () { expect(countDecimalPlaces(123e3), equals(0)); });
        test('failed handling scientific notation for 5e-10', () { expect(countDecimalPlaces(5e-10), equals(10)); });
        test('failed handling scientific notation for 6.78e3', () { expect(countDecimalPlaces(6.78e3), equals(0)); });
        test('failed handling scientific notation for 6.789e-2', () { expect(countDecimalPlaces(6.789e-2), equals(5)); });
    
        test('failed handling very small numbers 1e-100', () { expect(countDecimalPlaces(1e-100), equals(100)); });
        test('failed handling very small numbers 1.0000000001', () { expect(countDecimalPlaces(1.0000000001), equals(10)); });
        test('failed handling very large numbers 1e+100', () { expect(countDecimalPlaces(1e+100), equals(0)); });
        test('failed handling very large numbers 1000000000000.0', () { expect(countDecimalPlaces(1000000000000.0), equals(0)); });
    
        test('failed handling integer inputs 0', () { expect(countDecimalPlaces(0), equals(0)); });
        test('failed handling integer inputs 100', () { expect(countDecimalPlaces(100), equals(0)); });
      });
    
      group('addNumPrecision', () {
        test('failed adding two numbers with proper precision 1.234 + 2.345 expected closeTo(3.579, 1e-10)', () { expect(addNumPrecision(1.234, 2.345), closeTo(3.579, 1e-10)); });
        test('failed adding two numbers with proper precision 0.1 + 0.2 expected closeTo(0.3, 1e-10)', () { expect(addNumPrecision(0.1, 0.2), closeTo(0.3, 1e-10)); });
        test('failed adding two numbers with proper precision 123 + 0.456 expected closeTo(123.456, 1e-10)', () { expect(addNumPrecision(123, 0.456), closeTo(123.456, 1e-10)); });
    
        test('failed handling edge case addition with very small numbers 1e-10 + 2e-10 expected closeTo(3e-10, 1e-20)', () { expect(addNumPrecision(1e-10, 2e-10), closeTo(3e-10, 1e-20)); });
        test('failed handling edge case addition with very small numbers 1.0000000001 + 0.0000000001 expected closeTo(1.0000000002, 1e-10)', () { expect(addNumPrecision(1.0000000001, 0.0000000001), closeTo(1.0000000002, 1e-10)); });
    
        test('failed handling adding numbers with scientific notation 1.23e5 + 4.56e5 expected closeTo(5.79e5, 1e-5)', () { expect(addNumPrecision(1.23e5, 4.56e5), closeTo(5.79e5, 1e-5)); });
        test('failed handling adding numbers with scientific notation 5e-10 + 1.5e-10 expected closeTo(6.5e-10, 1e-20)', () { expect(addNumPrecision(5e-10, 1.5e-10), closeTo(6.5e-10, 1e-20)); });
      });
    
      group('roundToDecimalPlaces', () {
        test('failed rounding 0.123456789 to decimal places', () { expect(roundToDecimalPlaces(0.123456789, 3), 0.123); });
        test('failed rounding 9.123456789 to decimal places', () { expect(roundToDecimalPlaces(9.123456789, 3), 9.123); });
        test('failed rounding 98.123456789 to decimal places', () { expect(roundToDecimalPlaces(98.123456789, 3), 98.123); });
        test('failed rounding 987.123456789 to decimal places', () { expect(roundToDecimalPlaces(987.123456789, 3), 987.123); });
        test('failed rounding 9876.123456789 to decimal places', () { expect(roundToDecimalPlaces(9876.123456789, 3), 9876.123); });
      });
    }

These tests should show results like this:

PS C:\FlutterSDK\YourProject> flutter test test/util_math_test.dart
00:01 +28: All tests passed!

I am using VSCode ver 1.92.0, Dart ver v3.94.0, and Flutter ver v3.94.0.

Upvotes: 0

jamesdlin
jamesdlin

Reputation: 89975

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

Upvotes: 2

Greg Lowe
Greg Lowe

Reputation: 16261

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
    

Upvotes: 483

hashinclude72
hashinclude72

Reputation: 885

double roundOffToXDecimal(num number, {int precision = 2}) {
  var precisionWithPow10 = pow(10, precision);
  return (number * precisionWithPow10).round() / precisionWithPow10;

Upvotes: 0

luke77
luke77

Reputation: 3703

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

Upvotes: 6

SilSur
SilSur

Reputation: 509

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.

Upvotes: 0

Dani
Dani

Reputation: 4177

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

Upvotes: 1

Rahul Dev Garg
Rahul Dev Garg

Reputation: 49

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

Upvotes: 2

Shawn Ashton
Shawn Ashton

Reputation: 466

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

Upvotes: 6

Rens
Rens

Reputation: 745

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

Upvotes: 0

Nikolay
Nikolay

Reputation: 773

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

Upvotes: 0

CopsOnRoad
CopsOnRoad

Reputation: 267584

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
}

Upvotes: 112

Dinesh Prajapati
Dinesh Prajapati

Reputation: 47

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

Upvotes: -1

Michael
Michael

Reputation: 596

I prever converting my like this => `

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

`

Upvotes: -2

andyw
andyw

Reputation: 3763

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

Upvotes: 76

Abu Bakar
Abu Bakar

Reputation: 21

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

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

Upvotes: 2

Reza Taghizadeh
Reza Taghizadeh

Reputation: 477

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

(number * 100).round() / 100.0;

Upvotes: 17

Thanapol Thong-art
Thanapol Thong-art

Reputation: 43

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

 typeDecimal(data) => num.parse(data.toString()).toStringAsFixed(2);

Upvotes: 2

Mayb3Not
Mayb3Not

Reputation: 480

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

Upvotes: 2

Muhammad Umair Saqib
Muhammad Umair Saqib

Reputation: 1835

double value = 2.8032739273;
String formattedValue = value.toStringAsFixed(3);

Upvotes: 10

Sana&#39;a Al-ahdal
Sana&#39;a Al-ahdal

Reputation: 1780

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

Upvotes: 26

Rizwan Ansar
Rizwan Ansar

Reputation: 710

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

Upvotes: 38

Yngvar Natland
Yngvar Natland

Reputation: 1559

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

Upvotes: 137

Magnus
Magnus

Reputation: 18748

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.

Upvotes: 3

Hadi Mir
Hadi Mir

Reputation: 5133

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

Upvotes: 8

sultanmyrza
sultanmyrza

Reputation: 5392

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
}

Upvotes: 14

Runsis
Runsis

Reputation: 845

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

Upvotes: 5

Andrew Kawalya
Andrew Kawalya

Reputation: 13

This works pretty well

var price=99.012334554
price = price.roundTodouble();
print(price); // 99.01

Upvotes: -5

kubastick
kubastick

Reputation: 471

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

Upvotes: 23

Robert
Robert

Reputation: 5662

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

Upvotes: 33

Related Questions