【问题标题】:StreamTokenizer input number and parsed number are differentStreamTokenizer 输入数和解析数不同
【发布时间】:2019-01-21 23:01:59
【问题描述】:

我有一个接受数字的 StreamTokenizer。但是解析出来的数字和输入的不一样。

示例代码:

String str = "1000000000000.0000000000000";
double initial = 1000000000000.0000000000000;
InputStream in = new ByteArrayInputStream(str.getBytes());
StreamTokenizer input = new StreamTokenizer(new BufferedReader(new InputStreamReader(in)));
input.parseNumbers();
int n = input.nextToken();
if (n == StreamTokenizer.TT_NUMBER) {
    System.out.println("Original: " + str);
    System.out.println("Parsed: " + input.nval);
    System.out.println(initial + " == " + input.nval + " -> " + (initial == input.nval));
}

输出:

Original: 1000000000000.0000000000000
Parsed: 9.999999999999999E11
1.0E12 == 9.999999999999999E11 -> false

如何防止这种情况使两个double 值相等?

编辑:链接的问题讨论了为什么会出现此问题。我在问有哪些可能的方法来避免这个问题。

【问题讨论】:

  • StreamTokenizer::nextToken 的粗略检查表明,它尝试将double 中所有无小数点的数字的值累加,然后根据位数除以十的幂小数点的右边以获得正确的值。因此,“1000000000000.0000000000000”计算为10000000000000000000000000. / 10000000000000。这会导致有效数字中的位过多,并且会丢失精度。我能看到的唯一解决方案是避免StreamTokenizer
  • @ElliottFrisch 这个问题不是那个问题的重复。
  • @ElliottFrisch 这一切都很好,但在这种情况下,唯一以浮点表示的值是整数。在这种情况下问题就不同了。

标签: java token


【解决方案1】:

表示数字的一种准确方法是使用 BigDecimal。 Double 具有一定的精度,使用各种精度的双精度(例如:double1=10000.0 和 double2=0.0001)可能会导致 0.0001 被丢弃。 BigDecimal 避免了这种情况。

BigDecimal 的缺点:

  • 比较慢
  • 运算符 +、-、* 和 / 未重载

但是如果你在处理金钱或者必须要精确的话,你应该使用BigDecimal,否则你会蒙受损失。 例如:

String str = "1.0E12";
double initial = 1000000000000.0000000000000;
BigDecimal exVal = new BigDecimal(str);
System.out.println("Original: " + str);
System.out.println("Parsed: " + exVal);
System.out.println(initial + " == " + exVal + " -> " + (initial == exVal.doubleValue()));

程序输出:

Original: 1.0E12
Parsed: 1.0E+12
1.0E12 == 1.0E+12 -> true

【讨论】:

    【解决方案2】:

    根据 David Conrad 的分析,StreamTokenizer 类处理带小数点的数字存在缺陷,并且似乎没有解决方法。

    但这只是这个类的许多缺点缺陷之一。最好使用ScannerString.split。如果输入真的很复杂,请考虑使用解析器生成器来生成精确实现输入语法的词法分析器/解析器。

    相关错误:


    话虽如此,使用浮点数的应用程序应该能够容忍由舍入误差和不精确性引起的问题。关于比较浮点数的最佳方法有许多问答;例如

    【讨论】:

      猜你喜欢
      • 2021-04-23
      • 1970-01-01
      • 2020-07-30
      • 2017-07-02
      • 1970-01-01
      • 2013-12-05
      • 2021-12-16
      • 1970-01-01
      • 2011-10-26
      相关资源
      最近更新 更多