【问题标题】:How do you round a double in Dart to a given degree of precision AFTER the decimal point?如何在小数点后将 Dart 中的双精度数舍入到给定的精度?
【发布时间】: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

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

【问题讨论】:

    标签: dart precision double-precision


    【解决方案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 我相信这个答案 -> [stackoverflow.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

    【讨论】:

    【解决方案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

    【讨论】:

    【解决方案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 特有的。另见:

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

                        你应该改用:

                        • 整数。例如,如果您正在处理货币,请使用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
                            

                            【讨论】:

                              猜你喜欢
                              • 2022-12-13
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2010-11-29
                              • 1970-01-01
                              相关资源
                              最近更新 更多