【问题标题】:How do I increment a for loop using BigDecimal?如何使用 BigDecimal 增加 for 循环?
【发布时间】:2017-11-08 17:06:05
【问题描述】:

所以在编码方面我或多或少是个菜鸟。我正在为一门大学课程学习 java,我的一个练习要求我编写一个代码,使用左端点法、中点法和梯形法计算积分。

为此,我需要使用一个每次递增一步的 for 循环。我知道如何只使用双精度来做到这一点,但问题是,当我使用双精度数据类型运行计算时,它会给我带来舍入错误。阅读后(主要是查看其他堆栈溢出问题),我想我应该使用 BigDecimal 来消除舍入错误。

但是,使用 BigDecimal 会给我带来一大堆错误。据我所知,我不能使用普通运算符,例如 、== 等,因为 BigDecimal 不是原始数据类型。但是后来我不知道如何在仍然使用 BigDecimal 的同时进行递增的基本 for 循环。我在这里看到的另一件事建议使用 Commons-math? (第二个答案)

https://stackoverflow.com/questions/16707397/whats-wrong-with-this-simple-double-calculation#=

我不知道如何使用它,因为据我所知,它与简单的旧 import java.lang.Math 不相似。

所以我想我的问题是,我怎样才能让我的代码在 for 循环中使用 BigDecimal 工作,并且通常将数字比较在一起,或者我怎样才能使用 Common-Math 来完全避免舍入错误?

这是我的代码:

import java.util.Scanner;
import java.lang.Math;
import java.math.BigDecimal;

public class IntegrationMethods{

  //calculates the value of the function f(x)=x^2
  public static BigDecimal f(BigDecimal x){
    return x.multiply(x);
  }

  //uses a, b and N as arguments, calculates sum of integral using left 
endpoint rule
  public static BigDecimal leftEndpoint(BigDecimal a, BigDecimal b, 
BigDecimal n){
    BigDecimal h = (b.subtract(a)).divide(n);
    sum = new BigDecimal("0");
    i = new BigDecimal("0");
    for(i.compareTo(n)<=0;){
      sum = sum.add(h.multiply(f(a.add(h.multiply(i-1)))));
      i = i.add(new BigDecimal(1));
    }
    return sum;
  }

  public static BigDecimal midpoint(BigDecimal a, BigDecimal b, BigDecimal 
n){
    BigDecimal h = (b.subtract(a)).divide(n);
    BigDecimal sum = 0.0;
    for(BigDecimal i=0.0; i<n; i++){
      BigDecimal x1 = a.add(h.multiply(i));
      BigDecimal x2 = a.add(h.multiply(i+1));
      sum = sum.add(h.multiply((f(x1).add(f(x2))).divide(2)));
    }
    return sum;
  }

  public static void main(String[] args){
    Scanner scanner = new Scanner(System.in);

System.out.println("Please enter the lower limit of integration a.");
BigDecimal a = scanner.nextBigDecimal();

System.out.println("Please enter the upper limit of integration b.");
BigDecimal b = scanner.nextBigDecimal();

//swaps a and b so that the bigger of the two is always b.
//this prevents a negative popping up later on.
if(b<a){
  BigDecimal r = a;
  BigDecimal m = b;
  a = m;
  b = r;
}

System.out.println("Please enter the number of sampling points N.");
BigDecimal n = scanner.nextBigDecimal();

//checks if n is greater than or equal to 1, and asks for a new value if it isn't.
while (n<1.0){
  System.out.println("Please enter a new value for N.");
  n = scanner.nextBigDecimal();
}

System.out.println("For " + n + " intervals, the integral of x^2 using the left endpoint rule is: " + leftEndpoint(a,b,n));
System.out.println("Using the midpoint rule, the integral of x^2 is: " + midpoint(a,b,n));
  }
}

【问题讨论】:

  • 你为什么要尝试使用浮点值作为循环计数器?
  • 诀窍是阅读 BigDecimal 的 javadoc。 docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html
  • 这主要是因为我认为。我在我的一个计算中使用它,所以它也必须是 BigDecimal,然后 for 循环中的所有其他内容也必须是 BigDecimal,否则我会得到一个错误的操作数类型错误。
  • BigDecimal 专为用金钱计算而设计。对于物理/数学量,只需使用 double。通过在较大的术语之前将较小的术语相加来避免舍入错误。
  • 我不只是加法,我也在乘法和平方,这是我得到舍入错误的时候。我最初的一切都是双倍的,当我切换到 BigDecimal 时,我将如何避免那里的舍入错误?

标签: java bigdecimal


【解决方案1】:

从 0 计数到 19 的正常循环是:

for (int i = 0; i < 20; i++)
    System.out.println(i);

使用BigDecimal 完成的相同循环如下所示:

BigDecimal end = BigDecimal.valueOf(20);
for (BigDecimal i = BigDecimal.ZERO; i.compareTo(end) < 0; i = i.add(BigDecimal.ONE))
    System.out.println(i);

&lt;&gt;==等与BigDecimal一起使用的方法是使用compareTo()方法。 javadoc 展示了如何做到这一点:

此方法优先于六个布尔比较运算符(、>=、!=、(x.compareTo(y) 0),其中 是六个比较运算符之一。

所以,i &lt; end 写作i.compareTo(end) &lt; 0

还要注意BigDecimal0110BigDecimal.ZEROBigDecimal.ONEBigDecimal.TEN)预定义了常量,并且创建BigDecimal 整数值应该使用BigDecimal.valueOf(long val),而不是new BigDecimal(String val)

【讨论】:

  • 我了解 Javadoc,似乎每个人都认为我没有或没有阅读它,我的主要问题是将 compareTo 给出的整数值与 n 的 BigDecimal 值进行比较。跨度>
  • @LiziHutchinson 如果您认为需要将compareTo 返回的int 值与n 的值进行比较,那么您显然没有了解文档。 javadoc 明确显示您仅将int 值与0 进行比较。再次阅读 javadoc。 int 值的大小没有意义。仅值是== 0&lt; 0 还是&gt; 0
  • @LiziHutchinson 这是对“我不知道如何做一个基本的for循环在使用BigDecimal的同时增加”"*如何让我的代码工作* 在 for 循环中使用 BigDecimal,以及一般比较数字"*.这不是你要问的吗?
  • 对不起,我一直在编辑我的代码,我现在明白你的意思了。我有时仍然会在类型之间有点混淆。如果我想将 BigInteger 除以 2,我可以做 Int1.divide(new BigInteger int2="2") 还是必须单独声明 int2?
  • @LiziHutchinson 对于BigInteger,而不是BigDecimal,除以2,特别是最好使用shift:bi = bi.shiftRight(1)。要除以某个整数值,请使用bi = bi.divide(BigInteger.valueOf(intValue))。如果你除以相同的整数值很多,例如在循环中,您应该将BigInteger.valueOf(intValue) 分配给此类循环之外的变量。
【解决方案2】:

for 循环的每一部分都可以有你想要的任何表达式。第一个初始化变量,第二个必须计算为布尔值,第三个是任何表达式。比如:

for (BigDecimal i = BigDecimal.ZERO; i.compareTo(n) < 0; i = i.add(BigDecimal.ONE)) {
}

【讨论】:

  • i.add(BigDecimal.ONE) 什么都不做。请参阅my answer 以了解正确的循环。
  • @Andreas 谢谢你的收获。现在应该修好了。
【解决方案3】:

使用像BigDecimal 这样的浮点值作为循环计数器是个坏主意,因为它可能导致意外行为。您可以在此处阅读更多相关信息: https://www.securecoding.cert.org/confluence/display/java/NUM09-J.+Do+not+use+floating-point+variables+as+loop+counters

如果你将你的 for 循环计数器设为 int 类型,你可以将它包装在一个新的 BigDecimal 实例中以传递给 h.multiply() 并产生预期的结果:

for(int i=0; BigDecimal.of(i).compareTo(n) < 0; i++){
  BigDecimal x1 = a.add(h.multiply(BigDecimal.of(i)));
  BigDecimal x2 = a.add(h.multiply(BigDecimal.of(i+1)));
  sum = sum.add(h.multiply((f(x1).add(f(x2))).divide(2)));
}

【讨论】:

  • 虽然我使用的 n 也是一个 BigDecimal,所以当我编译它说错误的操作数类型时,这最终会给我一个错误。
  • 您提供的链接不适用于 BigDecimal,但我同意您的建议作为良好做法。
  • int i=0.0 不会编译。
  • BigDecimal不是一个近似的浮点值,循环使用BigDecimal是可以的。
  • @Andreas 修复了int i=0.0,谢谢。 @LiziHutchinson 我将更新我的解决方案以说明 n 是 BigDecimal
猜你喜欢
  • 2015-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-24
  • 2014-12-12
  • 2017-01-26
  • 2021-01-21
  • 2011-06-02
相关资源
最近更新 更多