【问题标题】:Making sure a string repr of decimal number is always n characters long确保十进制数的字符串 repr 始终为 n 个字符长
【发布时间】:2020-04-27 23:54:13
【问题描述】:

我需要确保我的所有十进制数字的长度始终最多为 15 个字符(包括点),同时尽可能保持精度。因此,它必须最多为 15 个字符,包括“.”。科学记数法的“E”和“-”。

我的想法是对大数使用科学计数法,对小数使用四舍五入。

例如,对于 1234567891234567.123456789,我会使用科学计数法,但对于 0.123456789123456789,我会四舍五入。

我寻找可以为我执行此操作的 java 库,但我找不到任何可以让我指定表示的字符总数的库。

感谢任何建议或指点。

编辑:更多想法 - 使用 24000E-18 可以无损失地表示 0.000000000000024 等数字。虽然例如 0.123456789123424 必须遭受一些损失,但这当然会使编写任何类型的简单分支算法变得更加困难。

EDIT2:我们用于传输数据的格式是字母数字格式,并将数据限制为总共 15 个字符。我必须编写代码来满足格式要求,这样才能在不触发格式错误的情况下传输数据,但仍要在限制范围内保持最大精度。

EDIT3:我正在使用它来测试我的功能,但到目前为止,在许多情况下一切都失败了:

  Random rnd = new Random();
  double n = 100000 + rnd.nextDouble() * 900000;
  String res;
  double rangeMin = -123456789123456D;
  double rangeMax = 123456789123456D;
  String val;
  for (int i=1;i<=1000;i++) {
    n = rangeMin + (rangeMax - rangeMin) * rnd.nextDouble();
    val = Double.toString(n);
    res = shorteningFunction(val);
    System.out.println(val + " " + val.length() + " " + res + " " + res.length());
  }

【问题讨论】:

  • 你应该使用BigDecimal然后设置你想要的精度。
  • @TimBiegeleisen 精度似乎不处理字符,而是处理数字。也许我错过了它,但我看不到如何限制字符总数?你介意重新打开我的问题吗,这似乎没有答案?
  • 数字不是单个字符吗?
  • @TimBiegeleisen 确实是,但它不计算点字符,或使用科学计数法时的“E”。
  • 有这个要求是没有意义的。这是否意味着并非所有数字都将以相同的精度存储?你真的想要吗?

标签: java string-formatting number-formatting decimalformat


【解决方案1】:

可以使用BigDecimal,对于整数部分BigInteger

/**
 * @param num number representation.
 * @param max the maximal length the result should have.
 * @return
 */
public static String truncateNumber(String num, int max) {
    num = num.replaceFirst("\\.0*$", "");
    BigDecimal x = new BigDecimal(num);

    // Large numbers - integral part
    String bigI = x.toBigInteger().toString();
    if (bigI.length() > max) {
        int expon10 = bigI.length() - max - 1; // - 1 for E

        // Digits after E:
        if (expon10 == 0) {
            ++expon10;
        } else {
            for (int p = expon10; p > 0; ++p) {
                ++expon10;
                p /= 10;
            }
        }
        x = x.movePointLeft(expon10);
        String plain = x.toPlainString().substring(0, max - 1 - expon10);
        return plain + "E" + expon10;
    }

    // Tiny numbers - 0.000 (as E-1 already requires 3 positions)
    String reprP = x.toPlainString();
    if (reprP.startsWith("-0.00")) {
        return truncateNumber(num.substring(1), max - 1);
    } else if (reprP.startsWith("0.00")) {
        String reprE = x.toEngineeringString(); // Does most work.
        int epos = reprE.indexOf('E');
        String mantissa = reprE.substring(0, epos);
        String exp = reprE.substring(epos);
        return mantissa.substring(0, Math.min(epos, max - exp.length())) + exp;
    }

    // Normal range - assumed in format 123.456, integral part in range
    String simple = x.toPlainString();
    if (simple.length() > max) {
        simple = simple.substring(0, max).replaceFirst("\\.0*$", "");
    }
    return simple;
}

这可能写得更好,以\.0* 结尾的子字符串,尤其是toPlainString 等的一些重复用法。太小的max 也会有害。

num 是否可能以科学/工程符号给出也是开放的。

【讨论】:

  • 这很好用,只是它通常会缩短太多:5.273913272867031E12 -> 5273913272867(例如从 20 到 13 个字符。)
  • @delica 很高兴知道,但我把确切的解决方案留给你。尤其是当你对这件事更深入的时候。祝你好运
  • 感谢部分解决方案!欣赏!
【解决方案2】:

我不希望发布不起作用的代码,因此我已经删除了杂乱无章的代码。

这是伪代码中的想法:

  1. 把它变成一个字符串。
  2. 检查长度
  3. 循环:
    • 如果长度太长:
    • 删除精度。
    • 把它变成一个字符串。
    • 检查长度。
  4. 返回

【讨论】:

  • 不错的答案,但 -123456789123456 的结果是 -1.2345678912346E+14(长度为 20)。
  • 确实这是一个很好的起点,谢谢! 0.00542222222299 没有变化(16 个字符)。
  • 我在刚刚的编辑中提供了一个测试功能。
  • @delica 我有一个问题。什么通常会使答案被接受?什么通常会使答案被标记为有用?
  • 我会说如果它有效,那么它被接受为答案。如果它不起作用,那么接受是没有意义的,因为其他人会认为它已经解决并且不再发布任何答案。
猜你喜欢
  • 2013-12-22
  • 1970-01-01
  • 1970-01-01
  • 2016-05-22
  • 2012-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多