簡體   English   中英

如何在小數點后將 Dart 中的雙精度四舍五入到給定的精度?

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

給定一個雙精度數,我想將它四舍五入到小數點后給定的精度點數,類似於 PHP 的 round() 函數。

我在 Dart 文檔中可以找到的最接近的東西是 double.toStringAsPrecision(),但這並不是我所需要的,因為它包括總精度點中小數點的數字。

例如,使用 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

隨着數字大小的增加,我相應地失去了小數點后的精度。

請參閱num.toStringAsFixed()的文檔。

字符串toStringAsFixed (int fractionDigits)

返回 this 的小數點字符串表示形式。

在計算字符串表示之前將其轉換為雙精度。

  • 如果 this 的絕對值大於或等於 10^21,則此方法返回由this.toStringAsExponential()計算的指數表示。

例子:

1000000000000000000000.toStringAsExponential(3); // 1.000e+21
  • 否則,結果是最接近的字符串表示形式,小數點后正好有 fractionDigits 數字。 如果 fractionDigits 等於 0,則省略小數點。

參數 fractionDigits 必須是滿足:0 <= fractionDigits <= 20 的整數。

例子:

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

num.toStringAsFixed()輪。 這會將您的 num (n) 轉換為具有您想要的小數位數 (2) 的字符串,然后在一行甜蜜的代碼中將其解析回您的 num:

n = num.parse(n.toStringAsFixed(2));

上述解決方案沒有適當地對數字進行四舍五入。 我用:

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

直接方式:

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

使用擴展:

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

用法:

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
}
var price = 99.012334554;
price = price.toStringAsFixed(2);
print(price); // 99.01

那是飛鏢的參考。 參考: https ://api.dartlang.org/stable/2.3.0/dart-core/num/toStringAsFixed.html

void main() {
  int decimals = 2;
  int fac = pow(10, decimals);
  double d = 1.234567889;
  d = (d * fac).round() / fac;
  print("d: $d");
}

打印:1.23

我使用了toStringAsFixed()方法,將數字四舍五入到小數點 EX 后的特定數字:

double num = 22.48132906

當我將它四舍五入為這樣的兩個數字時:

print(num.toStringAsFixed(2)) ;

它打印22.48

當我四舍五入到一個數字時,它打印出22.5

@andyw 使用 Dart 擴展方法修改后的答案:

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

用法:

var latitude = 1.123456;
var latitudeWithFixedPrecision = latitude.toPrecision(3); // Outputs: 1.123

要將 Dart 中的 double 舍入到小數點后的給定精度,您可以使用 dart toStringAsFixed()方法中的內置解決方案,但您必須將其轉換回 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
}

您可以簡單地將值乘以 100,然后將其四舍五入,然后再除以 100。

(number * 100).round() / 100.0;
double value = 2.8032739273;
String formattedValue = value.toStringAsFixed(3);

您可以使用toStringAsFixed來顯示小數點后的有限位數。 toStringAsFixed返回一個小數點字符串表示。 toStringAsFixed接受一個名為fraction Digits的參數,它是我們想要顯示的小數點后的位數。 以下是如何使用它。

double pi = 3.1415926;
const val = pi.toStringAsFixed(2); // 3.14

上述解決方案不適用於所有情況。 對我的問題有用的是這個解決方案,它將你的數字四舍五入(0.5 到 1 或 0.49 到 0)並且不帶任何小數

輸入: 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);

輸出: 13

此鏈接也有其他解決方案

您可以創建一個可重用的函數,該函數接受要格式化的 numberOfDecimal利用 toStringAsFixed() 方法格式化數字並將其轉換回 double

僅供參考,toStringAsFixed 方法不會對以 5 結尾的數字進行四舍五入(例如:toStringAsFixed 將 2.275 舍入為 2.27 而不是 2.28)。 這是 dart toStringAsFixed 方法的默認行為(類似於 Javascript toFixed)

作為一種解決方法,我們可以在最后一個十進制數之后將 1 添加到現有數字(例如:將 0.0001 添加到 2.275 變為 2.2751 & 2.2751 將正確四舍五入為 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;
    number += 1 / (10 * pow(10, existingNumberOfDecimal));
  }

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

// Example of usage:
var price = roundOffToXDecimal(2.275, numberOfDecimal: 2)
print(price); // 2.28

我在雙重上做了這個擴展

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

這是通過的測試。

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

只需在 double 上寫這個擴展

extension Round on double {
  double roundToPrecision(int n) {
    int fac = pow(10, n).toInt();
    return (this * fac).round() / fac;
  }
}

我認為接受的答案不是完美的解決方案,因為它轉換為string

如果您不想轉換為string並返回double使用GetX包中的double.toPrecision(decimalNumber)

如果你不想僅僅為此使用 GetX(我強烈推薦 GetX,它會改變你的生活)你可以復制並粘貼它。

當您想使用擴展時,請記住導入文件。

import 'dart:math';

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

如果你想在文本中舍入雙精度值。

文本('${carpetprice.toStringAsFixed(3)}',),

double (一個 IEEE-754二進制浮點數)四舍五入到特定數量的十進制數字本質上是有問題的。

就像 1/3 這樣的分數不能用有限個十進制數字精確表示一樣,許多(實際上是無限多個)十進制數字也不能用有限個二進制數字表示 例如,十進制數 0.1 不能用二進制精確表示。 雖然您可以嘗試將 0.09999 舍入為 0.1,但實際上將其“舍入”為double 聲稱以小數精度舍入double的大多數其他答案實際上返回最接近的可表示double

可以做的是使字符串表示看起來像一個漂亮的四舍五入數字,這就是double.toStringAsFixed()所做的。 這也是為什么當您打印 0.100000000... 時,如果實現嘗試打印用戶友好的值,您可能會看到0.1 但是,不要被愚弄了: double精度值實際上永遠不會是 0.1,如果你用這種不精確的值重復算術,你可能會累積錯誤

請注意,以上所有內容都是浮點數工作方式所固有的,並不是 Dart 特有的。 另見:

底線:如果您關心十進制精度,請不要使用二進制浮點類型 如果您正在處理金錢,這一點尤其重要。

您應該使用:

  • 整數。 例如,如果您正在處理貨幣,而不是使用double dollars = 1.23; , 使用int cents = 123; . 然后,您的計算將始終是准確的,並且只有在向用戶顯示它們時才能轉換為所需的單位(並且同樣可以在讀取用戶的輸入時以相反的方向轉換)。
  • 一種旨在以任意精度表示十進制數的類型。 例如, package:decimal提供了Decimal類型。 對於這種類型,其他一些答案(例如乘以 100、四舍五入,然后除以 100)將是合適的。 (但實際上你應該直接使用Decimal.round 。)

如果您在結果小數全為零時不想要任何小數,則可以使用以下方法:

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

如果輸入是9.004並且您想要 2 個小數,這將簡單地輸出9而不是9.00

如果使用動態類型的數據。 你可以使用它。

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

從沒想過這在 Dart 中如此復雜,但這是我的解決方案:

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

這個 DART 舍入問題由來已久( @LucasMeadows ),因為很明顯它直到現在還沒有得到充分解決(如@DeepShah 的觀察所示)。

眾所周知的舍入規則(未解決的問題):

"以數字 5 結尾的數字四舍五入:如果結果是偶數,則四舍五入;如果結果是奇數,則四舍五入。 "

所以這里是 DART 代碼解決方案:

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;
  print("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 ...
  print("Result (AccuRound) => $dResFin !!");   // Deactivate this 'print()' line in production code !!
  return dResFin;
}

這是一種完全手動的方法(可能有點矯枉過正),但它確實有效。 請測試它(用盡),如果我錯過了標記,請告訴我。

我發布了一個名為 Fixed 的包,它旨在解決這個問題。

https://pub.dev/packages/fixed

對不起,酒吧打算。

那個也是。

切勿在午夜后發帖。

如果你想使用特殊的舍入。 你可以試試這個功能(四舍五入)。

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

調試控制台:


[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

如果您需要適當的四舍五入(當第一個數字為 5 時向上)並且您想要尾隨 0,您可以使用此方法:

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

我喜歡這樣轉換我的 => `

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

`

您可以調用此函數來獲得黑暗(顫動)的精確度。 double eval -> double 想要轉換 int i -> 要返回的小數點數。

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

這工作得很好

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM