【问题标题】:Increment floating point numbers with the precision of the input with Java使用 Java 以输入的精度递增浮点数
【发布时间】:2021-05-07 18:13:59
【问题描述】:

我正在寻找以自己的精度递增浮点数的最优化且易于阅读的版本:

increment(1000) should return 1001
increment(100.1) should return 100.2
increment(0.1) should return 0.2
increment(0.01) should return 0.02
increment(0.001) should return 0.002
increment(0.0009) should return 0.0010
increment(0.000123) should return 0.000124
increment(increment(0.0009)) should return 0.002

可以通过字符串操作来完成,但我不想将其转换为字符串并将其解析回双精度。

我对字符串操作做了以下操作:

public static double incrementWithMover(double value){
    DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
    df.setMaximumFractionDigits(340); //340 = DecimalFormat.DOUBLE_FRACTION_DIGITS
    String valueString = df.format(value);
    String[] splitted = valueString.split("\\.");

    StringBuilder mover = new StringBuilder();

    if(splitted.length == 2){ // Floating Decimals
        int precision = splitted[1].length();
        df.setMaximumFractionDigits(precision);
        mover = new StringBuilder("0.");
        for(int i =1; i<precision; i++){
            mover.append("0");
        }

        mover.append("1");
    }
    else{   // Non Floating Decimals
        mover = new StringBuilder("1");
    }


    double incremented = Double.parseDouble(valueString) + Double.parseDouble(mover.toString());
    return Double.parseDouble(df.format(incremented));
}

我正在尝试编写此方法,因为我正在检查不同的值并尝试将所有值以它们自己的精度递增一个

编写这种incrementFloating 方法的最佳方法是什么?

【问题讨论】:

  • 如果数字是.000123,下一个增量应该是多少?
  • 应该是000124
  • 您必须将其保留为字符串。在您的示例中,没有等于 0.1 或 0.2 或任何其他值的双精度值。这个 BigDecimal 类可能会使整个工作变得更容易。
  • incrementFloating(incrementFloating(0.0009)) 应该返回什么?如果第一个增量返回0.0010,那么第二个增量应该返回0.002 还是0.0011
  • 应该是0.002

标签: java math floating-point double precision


【解决方案1】:

这可能对你有用。从双打改为字符串。

String[] vals = { "1000","1000.1", ".1", ".01", ".001", ".00123", ".0004" };
for (String v : vals) {
    System.out.printf("%s -> %s%n", v, incrementFloating(v));
}

打印

1000 -> 1001
1000.1 -> 1000.2
.1 -> 0.2
.01 -> 0.02
.001 -> 0.002
.00123 -> 0.00124
.0004 -> 0.0005

方法声明


public static String incrementFloating(String v) {  
    BigDecimal b = new BigDecimal(v);
    BigDecimal increment =
            BigDecimal.valueOf(1).scaleByPowerOfTen(-b.scale());
    return b.add(increment).stripTrailingZeros().toString();
}

【讨论】:

  • BigDecimal.valueOf(0.1) 已经不等于 0.1。
  • 这适用于我的用例,我将在我的项目中使用此代码。但是,我不确定这是否是我们进行此类操作的最佳选择。这是做这种工作最有效的方法吗?
  • 我认为字符串是最好的方法。顺便说一句,.0004 不起作用。试试看。所以可能有更好的解决方案。它使用字符串值工作。我将发布更正。
  • 关于效率。如果只有一种方法可以做到,那么无论花费多少时间都是有效的。但可能有更有效的方法来完成这项特定任务。
  • 使用精确的十进制值,正如您所描述的,您需要这样做,不会比这更有效。
【解决方案2】:

使用 Java 以输入的精度递增浮点数

这不是它的工作原理。

浮点数和双精度数并不像您想象的那样存储。

想象一下整个数轴。从负无穷到正无穷。

这条线有无数个整数值。在任意 2 个整数值之间,也存在无数个值。

计算机不是魔法。浮点数是 32 位,双精度数是 64 位。一个 32 位,根据基本数学,最多只能区分 2^32 个数字,也就是大约 40 亿。

40 亿是路,远小于 2 个无穷大的数量级

那么 是如何工作的呢?好吧,大约有 40 亿个数字是“祝福”的。这些数字可以用float 表示,没有其他数字。 0.3,例如,不祝福。 0.3 根本不是浮点数系统中的数字。它不存在。

那么,我该如何解释float x = 0.3; 有效,或者当您运行float x = 0.1 + 0.2; 时会发生什么?

好吧,float 和 double 操作会默默地转换为最近的祝福数字。

祝福数字的分布是基于二进制的(因此在十进制中它们没有任何特殊意义),并且分布不均。例如,在 1.0 附近,有远高于 100.0 的地方。

这意味着错误会潜入无处不在。您从根本上描述的操作在这里没有意义。你不能用花车或双打做你想做的事。期间。

使用字符串,或使用 BigDecimal。

如果你有兴趣,就去吧:

BigDecimal bd = new BigDecimal(0.3);
System.out.println(bd);
> 0.299999999999999988897769753748434595763683319091796875

这不是我编的。编写该代码并运行它。那个不敬的数字是什么?

这是最接近 0.3 的祝福数字。

因此,在双数系统中,应用您的算法,increment(0.3) 将尝试计算未受祝福的 0.299999999999999988897769753748434595763683319091796876,并且最接近的受祝福数字再次只是 0.299999999999999988897769753748434595763683319091796875,并且该操作会执行什么都没有。

没有意义。

Strings 或 BigDecimal,这是唯一的方法。从这个意义上说,效率是遥不可及的,但除非您打算每秒运行此操作数百万次,否则您不会注意到。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-03
    • 2011-07-12
    • 1970-01-01
    • 1970-01-01
    • 2012-04-15
    • 1970-01-01
    相关资源
    最近更新 更多