在Dart中如何将一个 double 四舍五入到小数点后的特定精度?

给定一个 double,我想将它舍入到精度为 小数点后 的给定点数,类似于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

随着数字大小的增加,小数点后的精度也相应降低。

260540 次浏览
void main() {
int decimals = 2;
int fac = pow(10, decimals);
double d = 1.234567889;
d = (d * fac).round() / fac;
print("d: $d");
}
< p >打印: 1.23 < / p >

请参阅num.toStringAsFixed ()的文档。

字符串toStringAsFixed(int fractionDigits)

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

在计算字符串表示形式之前,将此转换为double。

  • 如果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);
}
var price = 99.012334554;
price = price.toStringAsFixed(2);
print(price); // 99.01
这是dart的ref。 裁判:https://api.dartlang.org/stable/2.3.0/dart-core/num/toStringAsFixed.html < / p >
我使用了toStringAsFixed()方法,将一个数字四舍五入到小数点后的特定数字 例:< / p >
double num = 22.48132906

当我四舍五入到像这样的两个数字时:

print(num.toStringAsFixed(2)) ;

它输出22.48

当我四舍五入到一个数时,它输出22.5

使用Dart Extension方法修改@andyw的答案:

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

效果很好

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

要将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
}


上述解决方案并不适用于所有情况。对我的问题起作用的是这个解决方案,将你的数字(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

这个链接也有其他解决方案

你可以使用toStringAsFixed来显示小数点后的有限数字。toStringAsFixed返回一个小数字符串表示形式。toStringAsFixed接受一个名为fraction Digits的参数,它是我们想要显示的小数后面的位数。下面是如何使用它。

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

直接的方式:

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
}

如果你不想要任何小数,而结果的小数都是0,这样做是可行的:

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

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

把这个扩展写在double上

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

我认为公认的答案不是完美的解决方案,因为它转换为string

如果你不想转换为string和返回double使用 double.toPrecision(decimalNumber) from GetX package.

如果你不想为此使用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);
}
}

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

这是一个完全手动的方法(可能有点过度),但它是有效的。请测试一下(直到耗尽),如果我没有做到,请告诉我。

你可以创建一个你想格式化的接受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

如果使用动态类型的数据。你可以使用它。

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

您可以简单地将该值乘以100,然后四舍五入,然后再除以100。

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

如果你想在文本中舍入double值。

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

将一个double(一个IEEE-754 二进制浮点数)舍入为特定数量的小数数字本质上是有问题的。

同样地,像1/3这样的分数不能精确地用有限位数的十进制数字表示,许多(实际上是无穷多个)十进制数不能用有限数量的二进制数字表示。例如,十进制数0.1不能精确地用二进制表示。虽然您可以尝试将0.09999舍入到0.1,但作为double,它实际上会被“舍入”;到0.1000000000000000055511151231257827021181583404541015625。大多数其他声称以十进制精度舍入double的答案实际上返回可表示的最近的 double

可以所做的是使字符串表示形式看起来像一个漂亮的四舍五入的数字,这就是double.toStringAsFixed()所做的。这也是为什么当你打印0.100000000…,如果实现试图打印用户友好的值,则可能将看到0.1。然而,不要被愚弄:double的值实际上永远不会精确地为0.1,如果你用这种不精确的值重复计算,可以累积误差. 0将被替换为0.1。

请注意,以上所有内容都是浮点数工作方式所固有的,并不是Dart所特有的。还看到:

底线:如果你关心十进制精度,不要使用二进制浮点类型。如果你和钱打交道,这一点尤其重要。

相反,你应该使用:

  • 整数。例如,如果你处理的是货币,使用int cents = 123;而不是double dollars = 1.23;。这样,您的计算总是准确的,并且只有在将它们显示给用户时才能转换为所需的单位(同样,当从用户读取输入时,也可以反向转换)。
  • 一种类型,用于表示具有任意精度的十进制数。例如,package:decimal提供了一个Decimal类型。对于这样的类型,其他一些答案(例如乘以100、四舍五入,然后除以100)将是合适的。(但实际上你应该直接使用Decimal.round。)

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

这是通过的测试。

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

I prever convert my like this => ' < / p >

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

此函数可调用以获得黑暗(颤振)的精度程度。 Double eval >双倍的想要转换 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; }

如果你想要使用特殊的舍入。您可以尝试这个函数(舍入)。

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

从来没有想过这在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);