【问题标题】:Round a double to 3 significant figures [duplicate]将双精度数舍入为 3 位有效数字
【发布时间】:2011-11-24 19:38:17
【问题描述】:

有人知道我如何将双精度值四舍五入到 3 个有效数字,就像本网站上的示例一样

http://www.purplemath.com/modules/rounding2.htm

【问题讨论】:

  • 不是重复的。提到的问题是关于number of digits after decimal point。与significant figures 的概念不同。

标签: java rounding


【解决方案1】:

我通常不会对 number 本身进行四舍五入,而是在我需要显示数字的字符串表示时对其进行四舍五入,因为通常重要的是显示,需要四舍五入(尽管这可能不是在某些情况下是正确的,也许是你的,但如果是这样,你需要详细说明)。这样,我的号码保持其准确性,但它的显示被简化且更易于阅读。为此,可以使用 DecimalFormat 对象,例如使用“0.000”字符串 (new DecimalFormat("0.000")) 进行初始化,或使用 String.format("%.3f", myDouble),或其他几种方式。

例如:

// yeah, I know this is just Math.PI.
double myDouble = 3.141592653589793;
DecimalFormat myFormat = new DecimalFormat("0.000");
String myDoubleString = myFormat.format(myDouble);
System.out.println("My number is: " + myDoubleString);

// or you can use printf which works like String.format:
System.out.printf("My number is: %.3f%n", myDouble);

【讨论】:

  • 这是一个将十进制数字与有效数字混淆的错误答案。 @Sean Owen 的答案是正确的。
【解决方案2】:
double d = ...;
BigDecimal bd = new BigDecimal(d);
bd = bd.round(new MathContext(3));
double rounded = bd.doubleValue();

【讨论】:

  • 没有。或者打印取整后 % 0.001 的值并说明结果。
  • BigDecimal 的值打印为“0.00100”,完全正确。我建议你试试看。这里的 MathContext 与基数无关,BigDecimal 是一个任意精度的库。
  • 您提出了不同的观点,我认为这对 OP 没有帮助。最好的答案不是,“不,Java 什么也做不了”;就是上面的。 'rounded' 是最接近正确答案的双重表示。如果双精度是一个问题,那么整个问题确实没有意义!然后答案是无论如何都要使用 BigDecimal。
  • 我认为您正在阅读的问题是,“我怎样才能将一个实数完全表示为双精度数,将其四舍五入,并将答案完全表示为双精度数?”你就在那里:你不能。当然,我认为,问题的重点是“我如何在 Java 中四舍五入到 3 个有效数字,因为 Math.round() 会四舍五入到最接近的整数?”我认为这更有可能,而且,这个问题的答案不是“做不到”。
  • 尽管@EJP 的态度是正确的,但他的基本观点是正确的:向阅读此答案的任何人建议非常重要,他们可能希望留在BigDecimal - 不要执行转换为最接近的双精度值的最后一行。
【解决方案3】:

如何将 double 值四舍五入为 3 个有效数字

你不能。双打以二进制表示。它们没有要四舍五入的小数位。获得特定小数位数的唯一方法是将其转换为小数基数并将其保留在那里。将其转换回双精度的那一刻,您又失去了小数精度。 p>

对于这里和其他地方的所有粉丝,转换为其他基数并返回,或乘以和除以十的幂,请显示得到的双精度值 % 0.001 或任何所需的精度,并解释结果。

编辑:具体来说,这些技术的支持者需要解释以下代码的 92% 失败率:

public class RoundingCounterExample
{
    static float roundOff(float x, int position)
    {
        float a = x;
        double temp = Math.pow(10.0, position);
        a *= temp;
        a = Math.round(a);
        return (a / (float)temp);
    }

    public static void main(String[] args)
    {
        float a = roundOff(0.0009434f,3);
        System.out.println("a="+a+" (a % .0001)="+(a % 0.001));
        int count = 0, errors = 0;
        for (double x = 0.0; x < 1; x += 0.0001)
        {
            count++;
            double d = x;
            int scale = 2;
            double factor = Math.pow(10, scale);
            d = Math.round(d * factor) / factor;
            if ((d % 0.01) != 0.0)
            {
                System.out.println(d + " " + (d % 0.01));
                errors++;
            }
        }
        System.out.println(count + " trials " + errors + " errors");
    }
}

【讨论】:

  • 嗯,不。您将几件事混为一谈-您是否可以在Java中舍入到任意位置(是),表示任意精度实数(是),以及将任意精度实数表示为双精度(否)。我当然可以四舍五入;翻译中是否丢失了什么是另一个问题。我认为 OP 真的很想知道如何对实数进行舍入(尽管它被称为双精度数),这在 Java 中绝对是可能的。看我的回答。
  • @Sean Owen 您可以使用任何语言四舍五入到任意位置,只要这些位置的基数与数字的基数相同。我没有将任何关于任意精度实数的东西混为一谈,因为我没有提到它们。 “翻译中丢失的东西”是四舍五入。您不能将双精度数舍入到指定的小数位数。正如我为您的答案建议的简单测试清楚地表明的那样,这在术语上是矛盾的。
  • 根据您的逻辑,您也不能添加双打,因为总和并不完全正确。从抽象的意义上说是对的,但是,我很想看到你在一家软件公司尝试这个论点。 “老大,这个我实现不了,电脑上没办法加浮点数!”
  • 不是零,是的。我理解你的观点并解释了为什么它在这里似乎没有帮助。我不认为你解决了我的问题。 SO似乎认为我的回答更好,但很好的是你带来了更多的思考。
  • 好吧,我认为 OP(和我)可能真正想要的是获得双到 x 有效数字的字符串表示,所以在某种程度上你们都错了
【解决方案4】:
    public String toSignificantFiguresString(BigDecimal bd, int significantFigures ){
    String test = String.format("%."+significantFigures+"G", bd);
    if (test.contains("E+")){
        test = String.format(Locale.US, "%.0f", Double.valueOf(String.format("%."+significantFigures+"G", bd)));
    }
    return test;
}

【讨论】:

    【解决方案5】:

    如果你想手工做:

    import java.lang.Math;
    
    public class SigDig {
    
      public static void main(String[] args) {
        System.out.println("   -123.456   rounded up   to 2 sig figures is " + sigDigRounder(-123.456, 2,  1));
        System.out.println("     -0.03394 rounded down to 3 sig figures is " + sigDigRounder(-0.03394, 3, -1));
        System.out.println("    474       rounded up   to 2 sig figures is " + sigDigRounder(474, 2,  1));
        System.out.println("3004001       rounded down to 4 sig figures is " + sigDigRounder(3004001, 4, -1));
      }
    
      public static double sigDigRounder(double value, int nSigDig, int dir) {
    
        double intermediate = value/Math.pow(10,Math.floor(Math.log10(Math.abs(value)))-(nSigDig-1));
    
        if(dir > 0)      intermediate = Math.ceil(intermediate);
        else if (dir< 0) intermediate = Math.floor(intermediate);
        else             intermediate = Math.round(intermediate);
    
        double result = intermediate * Math.pow(10,Math.floor(Math.log10(Math.abs(value)))-(nSigDig-1));
    
        return(result);
    
      }
    }
    

    上述方法将双精度数舍入为所需的有效数字数,处理负数,并且可以明确告知向上或向下舍入

    【讨论】:

      【解决方案6】:

      Sean Owen (https://stackoverflow.com/a/7548871/274677) 给出的答案没有错。但是,根据您的用例,您可能希望得到一个字符串表示。在这种情况下,IMO 最好在 BigDecimal 空间中使用以下方法进行转换:

      bd.toPlainString();
      

      ...如果这是您的用例,那么您可能会对根据 Owen 的回答改编的代码将产生以下结果感到沮丧:

      d = 0.99, significantDigits = 3 ==> 0.99
      

      ...而不是更准确的0.990

      如果这些事情在您的用例中很重要,那么我建议对欧文的回答进行以下修改;您显然也可以返回 BigDecimal 本身,而不是调用 toPlainString() — 我只是为了完整性而提供这种方式。

      public static String setSignificanDigits(double value, int significantDigits) {
          if (significantDigits < 0) throw new IllegalArgumentException();
      
          // this is more precise than simply doing "new BigDecimal(value);"
          BigDecimal bd = new BigDecimal(value, MathContext.DECIMAL64);
          bd = bd.round(new MathContext(significantDigits, RoundingMode.HALF_UP));
          final int precision = bd.precision();
          if (precision < significantDigits)
          bd = bd.setScale(bd.scale() + (significantDigits-precision));
          return bd.toPlainString();
      }    
      

      【讨论】:

        【解决方案7】:

        双 d=3.142568; System.out.printf("答案:%.3f", d);

        【讨论】:

          猜你喜欢
          • 2010-09-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多