使用 BigDecimal 限制有效数字的任何巧妙方法

发布于 2024-12-07 05:43:25 字数 315 浏览 2 评论 0原文

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

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

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

我能想到的只是一些可怕的循环,除以 10 直到结果 <1,我希望有更好的方法。

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

I want to round a Java BigDecimal to a certain number of significant digits (NOT decimal places), e.g. to 4 digits:

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

etc. The basic problem is how to find the order of magnitude of the BigDecimal, so I can then decide how many place to use after the decimal point.

All I can think of is some horrible loop, dividing by 10 until the result is <1, I am hoping there is a better way.

BTW, the number might be very big (or very small) so I can't convert it to double to use Log on it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

莳間冲淡了誓言ζ 2024-12-14 05:43:25

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

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

Why not just use round(MathContext)?

BigDecimal value = BigDecimal.valueOf(123456);
BigDecimal wantedValue = value.round(new MathContext(4, RoundingMode.HALF_UP));
琉璃梦幻 2024-12-14 05:43:25

最简单的解决方案是:

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

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

The easierst solution is:

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

No String conversion is necessary, it is based purely on BigDecimal arithmetic and therefore as efficient as possible, you can choose the RoundingMode and it is small. If the output should be a String, simply append .toPlainString().

静谧幽蓝 2024-12-14 05:43:25

您可以使用以下行:

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

注意:此解决方案始终向下舍入到最后一位数字。

You can use the following lines:

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

Note: this solution always rounds down to the last digit.

季末如歌 2024-12-14 05:43:25

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

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

Someone will probably come up with a better solution, but the first thing that comes to mind is chuck it in to a StringBuilder, check whether it contains a '.' and return an appropriate length substring. E.g.:

int n = 5;
StringBuilder sb = new StringBuilder();
sb.append("" + number);
if (sb.indexOf(".") > 0)
{
    n++;
}
BigDecimal result = new BigDecimal(sb.substring(0, n));
一花一树开 2024-12-14 05:43:25

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

  1. 获取数字的字符串表示形式,“123.456789”
  2. 检索数字的前 N-1 位数字,“123.4”
  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)) 计算。

希望这有帮助。

To me this seems as simple as:
Given N = 5, D = 123.456789

  1. get the string representation of the number, "123.456789"
  2. retrieve the first N-1 digits of the number, "123.4"
  3. evaluate D[N] and D[N+1], in this case "5" and "6"
  4. 6 meets the criteria for rounding up (6 > 4), therefore carry 1 and make D[N] = 5+1 = 6
  5. D post rounding is now 123.46

Order can be calculated using Math.floor(Math.log(D)).

hope this helps.

小鸟爱天空丶 2024-12-14 05:43:25

由于 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

Since BigDecimal is basically a string, perhaps this:

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

Which produces this:

123238.756843723 has 6 digits to the left of the decimal
蓝颜夕 2024-12-14 05:43:25

AH的答案在技术上是正确的,但这里有一个更通用(也更容易理解)的解决方案:

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

A.H.'s answer is technically correct, but here is a more general (and easier to understand) solution:

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文