0

我创建了一个方法 calculate(String) 来评估 String 类型的操作数,例如“2+3”。我已经尝试了很多操作数并且该方法工作正常,除了这个:calculate("2.002*1000) //returns 2001.9999999999 我很好奇这个错误是怎么发生的。我已经通过代码很多次了,我不能找出问题所在。

这里的代码:

private String calculate(String str)
    {
        String num = "";
        List<String> list = new ArrayList<String>();
        for(int i = 0; i < str.length(); i++) //Arrange str into list, elements are seperated by "+" or "-"
        {
            if(! checkPM(str.substring(i, i + 1)))
            {
                num = num + str.substring(i, i + 1);
            } else
            {
                list.add(num);
                list.add(str.substring(i, i + 1));
                num = "";
            }
        }
        list.add(num);//add the last num into list
        if(checkSign(list.get(list.size() - 1)))//remove last element if it is an operator
        {
        list.remove(list.get(list.size() - 1));
        }
        String numlistele = ""; //Elements of numlist
        List<String> numlist = new ArrayList<String>(); //List of numbers to be TD
        List<String> TDlist = new ArrayList<String>(); //List of times or divide
        for(int j = 0; j < list.size(); j++) //Check which of the elements of list contains "*" or "/"
        {
            String tdAns = ""; //Answer of numlistele timed or divided
            if(checkTD(list.get(j))) // When the elements of list contains "*" or "/"
            {
                for(int k = 0; k < list.get(j).length(); k++) //
                {
                    if(! checkSign(list.get(j).substring(k, k + 1)))
                    {
                        numlistele = numlistele + list.get(j).substring(k, k+1);
                    } else
                    {
                        numlist.add(numlistele);
                        TDlist.add(list.get(j).substring(k, k+1));
                        numlistele = "";
                    }
                }
                numlist.add(numlistele); //Adds the last number into numlist
                numlistele = ""; //Restore numlistele to "", to be used in next loop 
                tdAns = numlist.get(0); //Answer of numlistele timed or divided, firstly it is equals to the first elements of numlist
                for(int l = 0; l < TDlist.size(); l++)
                {
                    if(TDlist.get(l).equals("×"))
                    {
                        double tempdou = Double.parseDouble(tdAns) * //temporary double used to save to tdANS
                                Double.parseDouble(numlist.get(l+1));
                        tdAns = String.valueOf(tempdou);
                    } else //when TDlist.get(l).equals("/") is true
                    {
                        double tempdou = Double.parseDouble(tdAns) / 
                                Double.parseDouble(numlist.get(l+1));
                        tdAns = String.valueOf(tempdou);
                    }
                }
            list.set(j, tdAns);
            }               
            numlist.clear(); //Clear numlist for next loop
            TDlist.clear(); //Clear TDlist for next loop
        }
        String ans = list.get(0); //Will become final answer later, first it is assign to first element of list
        for(int m = 0; m < list.size(); m++)
        {
            if(list.get(m).equals("+"))
            {
                double tempdou = Double.parseDouble(ans) + //Temporary double used to save to ans
                        Double.parseDouble(list.get(m + 1));
                ans = String.valueOf(tempdou);
            } else if(list.get(m).equals("-"))
            {
                double tempdou = Double.parseDouble(ans) - 
                        Double.parseDouble(list.get(m + 1));
                ans = String.valueOf(tempdou);
            }
        }
        if(ans.length() > 2)
        {
            if(ans.substring(ans.length() - 2).equals(".0")) //To remove .0 of the answer
            {
                ans = ans.substring(0, ans.length() - 2);
            }

        }
        return ans;
    }

提前致谢。

4

3 回答 3

6

浮点表示并不总是那么准确。当您解析字符串“2.002”时,Double.parseDouble返回最接近的可能double表示形式。

当乘以 时1000,浮点表示中的误差变得大到足以注意到 (2001.999999999999)。

这是一篇关于浮点表示和可能发生的事故的优秀在线文章。

为确保浮点数的准确表示,您可以使用 Java 的内置BigDecimal类。它可能没有那么快,但它会保留您需要的所有精度。

于 2013-07-09T17:57:17.140 回答
4

发生这种情况是因为由于 IEEE 754 舍入规则而对双精度数进行了舍入。如果您不想四舍五入,请使用 BigDecimal 类。

http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html

于 2013-07-09T17:59:01.313 回答
2

这是浮点数学的教科书案例。你乘的实际上不是 2.002 * 1000。相反,它是一个接近 2.002 的数字乘以一个接近 1000 的数字。

如果你需要绝对精度,你应该使用 java 的BigDecimal类型。在 C# 中,您将使用decimal类型。

http://en.wikipedia.org/wiki/Round-off_error

http://en.wikipedia.org/wiki/Floating_point

于 2013-07-09T17:57:11.417 回答