【问题标题】:Any neat way to limit significant figures with BigDecimal使用 BigDecimal 限制有效数字的任何巧妙方法
【发布时间】:2011-09-27 16:11:50
【问题描述】:

我想将 Java BigDecimal 舍入到一定数量的有效数字(不是小数位),例如到 4 位数:

12.3456 => 12.35 123.456 => 123.5 123456 => 123500

等等。基本问题是如何找到BigDecimal的数量级,这样我就可以决定在小数点后使用多少位。

我能想到的只是一些可怕的循环,除以 10 直到结果为

顺便说一句,这个数字可能非常大(或非常小),所以我无法将其转换为双精度以使用登录。

【问题讨论】:

    标签: java bigdecimal


    【解决方案1】:

    为什么不直接使用round(MathContext)

    BigDecimal value = BigDecimal.valueOf(123456);
    BigDecimal wantedValue = value.round(new MathContext(4, RoundingMode.HALF_UP));
    

    【讨论】:

    • MathContext 对我来说是一个盲点,但在这里它是一个很好的匹配。
    • 嗯,它只是封装了想要的精度和舍入模式,仅此而已。
    • 我从来没有需要它,但我渴望内置封装 scale 和舍入模式。
    • @A.H.直到您尝试除以 1/3 并抛出异常,因为它永远重复时,您才需要。就像我刚刚发生的那样:)虽然默认行为是有意义的,因为它应该是无限精确的。
    • @KennyCason:我不明白你的意思。如果我使用divide(divisor, scale, ROUNDING_MODE),那么就有一个明确的结束。在这种情况下,计算的结束由 scale 定义,而不是由 precision 定义。我的声明是,MathContext 不允许轻松封装 scale 和舍入模式,仅适用于 precision 和舍入模式。
    【解决方案2】:

    最简单的解决方案是:

      int newScale = 4-bd.precision()+bd.scale();
      BigDecimal bd2 = bd1.setScale(newScale, RoundingMode.HALF_UP);
    

    不需要字符串转换,它纯粹基于BigDecimal算法,因此尽可能高效,您可以选择RoundingMode,它很小。如果输出应该是String,只需附加.toPlainString()

    【讨论】:

    • 这似乎在所有情况下都能正常工作,即使科学记数法适用于大小值。唯一的小问题是,如果结果没有小数部分,它会转换为 123 而不是 123.0(我更喜欢我的应用程序)这样的字符串,但这当然非常容易修复。谢谢。
    • 感谢您接受我的回答。在我的机器上,“123”和“4 位”“123.0”的输出。有什么区别?
    • 哦,我刚刚注意到我的代码也使用了 stripTrailingZeros() - 很抱歉造成混淆。
    • 因为 '123' 到两个 sig 无花果是 1.2 x 10,'120' 也是如此。 '120.0' 是 4 位有效数字。
    • 另一种使用内置方法的方法:stackoverflow.com/questions/5474742/…
    【解决方案3】:

    您可以使用以下几行:

    int digitsRemain = 4;
    
    BigDecimal bd = new BigDecimal("12.3456");
    int power = bd.precision() - digitsRemain;
    BigDecimal unit = bd.ulp().scaleByPowerOfTen(power);
    BigDecimal result = bd.divideToIntegralValue(unit).multiply(unit);
    

    注意:此解决方案总是向下舍入到最后一位。

    【讨论】:

    • 不知道 BigDecimal 做到了这一点。很好的解决方案。
    【解决方案4】:

    可能有人会想出更好的解决方案,但首先想到的是将其放入 StringBuilder 中,检查它是否包含 '.'并返回适当长度的子字符串。例如:

    int n = 5;
    StringBuilder sb = new StringBuilder();
    sb.append("" + number);
    if (sb.indexOf(".") > 0)
    {
        n++;
    }
    BigDecimal result = new BigDecimal(sb.substring(0, n));
    

    【讨论】:

    • 这看起来不错,虽然也需要一些四舍五入。
    • 谢谢,这比我的想法好。
    【解决方案5】:

    对我来说,这似乎很简单: 给定 N = 5,D = 123.456789

    1. 获取数字的字符串表示,“123.456789”
    2. 检索数字“123.4”的前 N-1 位
    3. 计算 D[N] 和 D[N+1],在本例中为“5”和“6”
    4. 6 满足向上取整的标准 (6 > 4),因此进位 1 并使 D[N] = 5+1 = 6
    5. D 后舍入现在是 123.46

    可以使用 Math.floor(Math.log(D)) 计算顺序。

    希望这会有所帮助。

    【讨论】:

    • 他特别说他不能使用Math.log()。
    • @Thor84no - 啊,错过了。
    【解决方案6】:

    因为 BigDecimal 基本上是一个字符串,所以可能是这样的:

    import java.math.BigDecimal;
    
    public class Silly {
        public static void main( String[] args ) {
            BigDecimal value = new BigDecimal("1.23238756843723E+5");
            String valueString = value.toPlainString();
            int decimalIndex = valueString.indexOf( '.' );
            System.out.println( value + " has " +
                (decimalIndex < 0 ? valueString.length() : decimalIndex) +
                " digits to the left of the decimal" );
        }
    }
    

    产生这个:

    123238.756843723 has 6 digits to the left of the decimal
    

    【讨论】:

      【解决方案7】:

      A.H.'s answer 在技术上是正确的,但这里有一个更通用(也更容易理解)的解决方案:

      import static org.bitbucket.cowwoc.requirements.core.Requirements.assertThat;
      
      /**
       * @param value            a BigDecimal
       * @param desiredPrecision the desired precision of {@code value}
       * @param roundingMode     the rounding mode to use
       * @return a BigDecimal with the desired precision
       * @throws NullPointerException if any of the arguments are null
       */
      public BigDecimal setPrecision(BigDecimal value, int desiredPrecision, RoundingMode roundingMode)
      {
          assertThat("value", value).isNotNull();
          assertThat("roundingMode", roundingMode).isNotNull();
          int decreaseScaleBy = value.precision() - desiredPrecision;
          return value.setScale(value.scale() - decreaseScaleBy, roundingMode);
      }
      

      【讨论】:

        猜你喜欢
        • 2016-06-03
        • 1970-01-01
        • 2011-03-20
        • 2011-05-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-12
        相关资源
        最近更新 更多