如何在小数点后将 Dart 中的双精度数舍入到给定的精度?

Posted

技术标签:

【中文标题】如何在小数点后将 Dart 中的双精度数舍入到给定的精度?【英文标题】:How do you round a double in Dart to a given degree of precision AFTER the decimal point? 【发布时间】:2015-04-09 18:03:28 【问题描述】:

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

随着数字的大小增加,我相应地失去了小数点后的精度。

【问题讨论】:

【参考方案1】:

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

字符串 toStringAsFixed(int fractionDigits)

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

在计算字符串表示之前将其转换为双精度。

如果 this 的绝对值大于或等于 10^21,则此方法返回由 this.toStringAsExponential() 计算的指数表示。

例子:

1000000000000000000000.toStringAsExponential(3); // 1.000e+21
否则,结果是最接近的字符串表示形式,小数点后正好有 fractionDigits 位。如果 fractionDigits 等于 0,则省略小数点。

参数 fractionDigits 必须是满足:0

例子:

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

【讨论】:

必须是公认的+参考+解释+示例 我认为 toStringAsFixed() 是由于其不一致而使用的舍入方法。例如,尝试5.550.toStringAsFixed(1) 在舍入到 0 位时也要小心,因为在这种情况下,表达式返回 int 而不是 double,例如num.parse(50.123.toStringAsFixed(0)) is int - 这可以通过在其末尾添加 .toDouble() 来解决。 @brendan 假设 尝试 存储 5.55 的 double 实际上是 5.5499999999999998... 那么toStringAsFixed(1) 返回正确答案 (5.5)。【参考方案2】:

num.toStringAsFixed() 回合。这会将您的 num (n) 转换为具有您想要的小数位数 (2) 的字符串,然后在一行代码中将其解析回您的 num:

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

【讨论】:

这是最快的方法。但是,dart 没有直接的方法来做到这一点,这很愚蠢 扩展方法:extension NumberRounding on num num toPrecision(int precision) return num.parse((this).toStringAsFixed(precision)); @ThinkDigital Dart 确实有一个直接的方法:toStringAsFixed。它确实没有有直接的方法将double四舍五入到指定的小数位数并将结果保持为double,因为这是一件愚蠢的事情.您想要的十进制值很可能无法完全表示为 double 我认为这个答案应该澄清这段代码的实际作用是将 最近的 double 返回到四舍五入的值。【参考方案3】:

上述解决方案没有适当地对数字进行四舍五入。我用:

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

【讨论】:

确认,这轮 dp(5.550, 1) 正确到 5.6 - 不像最流行的答案,它给出了一个稍微不正确的答案 5.5 正如@brendan 指出的那样。 不适用于 dp(73.4750, 2) 它返回 73.47 而不是 73.48 calculatorsoup.com/calculators/math/… 查看此链接 @DeepShah 我相信这个答案 -> [***.com/a/66840734/3002719] 解决了您对“number 5 rounding rule”问题的担忧以上。 这本质上是有问题的,因为将 IEEE-754 浮点数(二进制)四舍五入到十进制精度是没有意义的;许多小数十进制数不能用二进制表示。另见Is floating point math broken?。 能否将 4436.605 转换为 4436.61(有 2 个位置)【参考方案4】:

直接方式:

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

【讨论】:

我不明白这是如何工作的。为什么 double.parse 会返回 double ,因为我们没有传递对它的引用? (我在想this.parse(toStringAsFixed(n));之类的东西) @kyu double.parsestatic 类型的 static 方法。 我认为这个答案应该澄清这段代码的实际作用是将 最近的 double 返回到四舍五入的值。【参考方案5】:
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

【讨论】:

这个答案不会给Greg Lowe's much earlier answer添加任何东西。【参考方案6】:
void main() 
  int decimals = 2;
  int fac = pow(10, decimals);
  double d = 1.234567889;
  d = (d * fac).round() / fac;
  print("d: $d");

打印: 1.23

【讨论】:

这是目前最好的方法。四舍五入到像 0.01 这样本身不是双精度的精度并不是微不足道的。结果甚至可能根本无法表示为双精度数。我强烈建议对小数精​​度很重要的任何事物(例如金钱)使用整数。 另外值得指出的是,如果编译成 js,那么 Dart 整数也将开始失去精度,你需要使用如下包:pub.dartlang.org/packages/bignum 这种方法感觉是目前最好、最优雅的方法。 double 不能赋值给 int 错误发生在第 3 行【参考方案7】:

我使用toStringAsFixed() 方法,将数字四舍五入为小数点后的特定数字 例如:

double num = 22.48132906

当我将它四舍五入为这样的两个数字时:

print(num.toStringAsFixed(2)) ;

它打印了22.48

当我四舍五入到一个数字时,它打印出22.5

【讨论】:

这个答案不会给Greg Lowe's much earlier answer添加任何东西。【参考方案8】:

@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

【讨论】:

【参考方案9】:

要将 Dart 中的双精度数舍入到小数点后的给定精度,可以使用 dart toStringAsFixed() 方法中的内置解决方案,但必须将其转换回双精度

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


【讨论】:

此答案不会向Yngvar Natland's much earlier answer 添加任何内容。另外,我认为这个答案应该澄清这段代码实际上所做的是将 nearest double 返回到舍入值。【参考方案10】:

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

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

【讨论】:

【参考方案11】:
double value = 2.8032739273;
String formattedValue = value.toStringAsFixed(3);

【讨论】:

请为您的答案添加更多解释,以造福其他用户 这在语法上不正确,toStringAsFixed 带了一个参数【参考方案12】:

您可以使用toStringAsFixed 来显示小数点后的有限位数。 toStringAsFixed 返回小数点字符串表示。 toStringAsFixed 接受一个名为 fraction Digits 的参数,这是我们想要显示的小数点后的位数。这是它的使用方法。

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

【讨论】:

【参考方案13】:

上述解决方案并非适用于所有情况。对我的问题有用的是这个解决方案将四舍五入您的数字(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

This link has other solutions too

【讨论】:

【参考方案14】:

您可以创建一个可重复使用的函数,该函数接受要格式化的 numberOfDecimal,并利用 toStringAsFixed() 方法格式化数字并将其转换回双精度数

仅供参考,toStringAsFixed 方法不会对以 5 结尾的数字进行四舍五入(例如:toStringAsFixed 将 2.275 舍入为 2.27 而不是 2.28)。 This is the default behaviour of dart toStringAsFixed method (similar to 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

【讨论】:

【参考方案15】:

只需在 double 上写这个扩展

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

【讨论】:

但是您显然需要对int fac 做点什么。 pow() 返回 num,而不是 int。所以,toInt().【参考方案16】:

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

如果您不想转换为 string 并转换回 double,请使用 double.toPrecision(decimalNumber) 来自 GetX 包。

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

【讨论】:

【参考方案17】:

如果你想对文本内的双精度值进行四舍五入。

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

【讨论】:

【参考方案18】:

如果您在结果小数全为零时不想要任何小数,则可以使用以下方法:

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

【讨论】:

【参考方案19】:

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

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

【讨论】:

【参考方案20】:

我在 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 removes trailing zeros
  /// and sometimes periods
  /// example 5.0  -function-> "5"
  String roundedPrecisionToString(int places) 
    double mod = pow(10.0, places) as double;
    String doubleToString = ((this * mod).round().toDouble() / mod).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:player_agent/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");
    );
  );

【讨论】:

【参考方案21】:

double(一个 IEEE-754 二进制 浮点数)四舍五入到特定数量的十进制 位数本身就是有问题的。

就像 1/3 这样的分数不能用有限个十进制数字精确表示一样,很多(实际上是无限多个)十进制数也不能用有限个二进制数字。例如,十进制数 0.1 不能用二进制精确表示。虽然您可以尝试将 0.09999 舍入为 0.1,但实际上是 0.1000000000000000055511151231257827021181583404541015625。大多数声称以十进制精度舍入 doubles 的其他答案实际上返回 最近 可表示的 double

可以做的是对字符串表示中的值进行四舍五入,这就是double.toStringAsFixed()所做的。这也是为什么当您打印 0.1 时,如果实现尝试打印用户友好的值,您可能会看到 0.1。但是,请不要上当:double 的值实际上永远不会是 0.1,如果你用这样不精确的值重复算术,你会累积错误

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

Is floating point math broken? 如果不理解以上解释,Tom Scott 也发a great video explaining how floating-point works。

底线:如果您关心 十进制 精度,请不要使用 二进制 浮点类型。如果您在处理金钱问题,这一点尤其重要。

你应该改用:

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

【讨论】:

使用此包精确舍入:Decimal.parse(this.toString()).round(scale: places).toDouble() @Paul 你应该去掉toDouble()。这违背了使用package:decimal 的意义。 可能会有双重回报被质疑的情况,所以我想我应该添加它。另外:这对双精度进行了正确的舍入(可能只有一次)。但是,是的,如果您需要使用双精度/小数进行计算,您显然不应该这样做。 @Paul “正确舍入一个双精度数”是一个无意义的陈述,因为您不能以基数 10 位数字精确舍入 double。从根本上说,有一些以 10 为底的数字不能精确地表示为 doubles。没有“四舍五入”可以避免这种情况。【参考方案22】:

这个 DART 舍入问题由来已久 (@LucasMeadows),因为很明显它直到现在还没有得到充分解决 (as indicated by @DeepShah's observation)。

众所周知的舍入规则(未解决的问题):

" 以数字 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;

这是一种完全手动的方法(可能有点矫枉过正),但它确实有效。请测试它(用尽),如果我错过了标记,请告诉我。

【讨论】:

更新函数(上图):roundAccurately(73.4750, 2) 返回73.48 !! @deep-shah 这非常复杂。看看这个答案,只使用:Decimal.parse(this.toString()).round(scale: places).toDouble()【参考方案23】:

我喜欢这样转换我的 => `

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

`

【讨论】:

【参考方案24】:

你可以调用这个函数来获得黑暗(颤动)的精确度。 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; 

【讨论】:

【参考方案25】:

这很好用

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

【讨论】:

以上是关于如何在小数点后将 Dart 中的双精度数舍入到给定的精度?的主要内容,如果未能解决你的问题,请参考以下文章

将双精度数舍入到 x 有效数字

如何舍入双精度值但保持尾随零

如何优化 spark 函数以将双精度值舍入到小数点后 2 位?

如何在 Torch 的 GPU 上将张量的元素限制/舍入到小数点后 4 位?

变体浮点到双精度值转换,舍入到小数点后 1 位 [重复]

SQL中的取整函数取小数