【问题标题】:Float greater or less than zero浮动大于或小于零
【发布时间】:2012-04-30 15:50:53
【问题描述】:

我有以下代码,有时返回 true,有时不返回。

知道是什么导致了不同的结果吗?

0.00 字符串来自 JSON 对象。

(代码简化)

if(new Float("0.00")>0){
    // do something
}

编辑:

我有一些浮点数,我想确定它是零、小于零还是大于零。这些值可能是 0.00、0.001 或 -0.001。如何确定它们是正数、负数还是零?

编辑:

也许我应该澄清我是如何获得这些值的。我可能完全是导致问题的其他原因,因为我阅读了 BigDecimal 并尝试使用它无济于事。

使用以下代码从 JSON 提要(格式为 LT:0.000)中提取值:

price = new BigDecimal(stocksJSONArray.getJSONObject(i).getString("LT"));

然后为了测试价格是大于还是小于零,我使用了以下条件语句:

if(price.compareTo(BigDecimal.ZERO)==1){
    // greater than zero
}
else if(price.compareTo(BigDecimal.ZERO)==-1){
    // less than zero
}

此代码循环用于从 JSON 提要中读取的许多值。从结果来看,一些为零的price 被处理为大于零,而一些被处理为小于零。我怀疑是其他原因导致了这里的问题?

我还做了一个测试,看看问题是否出在数据的准确性上。所以我这样做了:

DecimalFormat frmt = new DecimalFormat("0.000000000000000000000000");
String formatted = frmt.format(stock.change);

对于被识别为正数和负数的零,它的跟踪值仍然是0.000000000000000000000000,而不是0.000000000000000000000001 或类似的东西。

【问题讨论】:

  • 给出的答案是正确的,但在您的情况下结果不应该改变。您确定 0.00 总是按原样解析吗?而且你确定float初始化的值不是其他float操作的结果吗?
  • 详情请见What Every Computer Scientist Should Know About Floating-Point Arithmetic。请注意,我删除了我的答案,因为我所说的大部分内容显然是错误的。在这件事上还有很多专家。

标签: java android


【解决方案1】:

您的表达式将可靠地在 Java 中产生 false 的结果。

但是,假设零是例如 -1 除以正无穷大的结果。在这种情况下,它将在内部表示为,粗略地说,-0.00。在某些情况下,它仍会打印为零(没有减号),在其他情况下,它的行为与 0.00 不同。

浮点数通常可以用与整数相同的方式比较小于 - 存在舍入错误的风险,但添加或减去随机小值无助于该错误。它与相等比较不同。

我建议您仔细检查您的事实和 do more reading 关于浮点行为的信息。

编辑:我在上面大大简化以回答原始问题。要回答对问题的修改,我们需要更深入。

对于浮点数的任何操作,都应该知道并考虑输入的精度准确度,以及所需的准确度 的输出。有时任务是可解决的,有时则不是 - 输入的准确性可能不足以产生答案。

在您的情况下,precision 是 32 位,其中 24 位是尾数和 8 位指数。这意味着此数据类型可以安全地将 0.001 与 0.00100001 区分开来,但不能与 0.001000001 区分开来,正如您所见:

 System.out.println((float)0.001 < (float)0.001000001);

(请注意,如果您不通过强制转换强制进行单精度比较,则会得到不同的结果。在这种情况下,计算将以双精度完成并且数字将被安全区分 - 直到您将它们更接近.)

因此,精度由数据类型决定。没有那么准确。确定输入精度通常比精度更具挑战性,因为它与数据类型无关,只是精度永远不会比精度更好。

一个数学实数可以在四种可能的情况下发现自己在特定浮点类型中的可表示性,这些情况对应于它作为文字出现时所接受的不同处理,以人类可读的十进制表示法。

  • 它可以用二进制准确表示。例如,0 或 0.25。那么它与整数变量中的整数一样准确。
  • 或者它可以近似地表示,其准确度对应于类型的精度。例如,1/3 或 0.1 或 0.001。当所需的指数适合可用的指数位数时,就会发生这种情况,但是当数字的二进制扩展比尾数长或完全无限时。
  • 或者它可以近似表示,但准确度严重失真。这些是denormal(低于正常值)数字。它不仅不准确,它的算术可能会慢下来,这通常记录了正确的行为,即使是一个受人尊敬的 Java 编译器也可能会在看到这种类型的文字时有点出汗,不过是 bug
  • 或者它根本不适合,编译器会拒绝字面量太大。

因此,在您的情况下,我们只有三个有效输入:0(准确)、0.001(近似)和 -0.001(近似),这使您的问题可以解决。 只需将您的数字与 0 字面值进行比较(顺便说一下,这是准确的),您将始终得到预期的布尔值(完全准确的输出)。

然而,这取决于您的输入是否直接来自文字。如果您的输入是 0.001、-0.001 和 (float)1000 * (float)0.001 - 1 之一,这将是一个不同的问题,您必须得到答案,例如:

if (-0.00001 < x && x < 0.00001) // then x is zero 

如果您允许任何输入,而不仅仅是这三个魔法值,并且不知道输入的准确性,那么这就是不可能完成的任务。即使是一个以 0.000000000... 开头的文字,最后还有一些垃圾数字,Java 编译器也会将其转换为完全中性的零,在这种情况发生之后,再多的 Java 代码都无法从准确而美丽的 @ 中分辨出来。 987654329@,或者如果您在下溢值中添加减号会发生什么。都是相同的,不准确的零,变量中相同的位模式,而不是 3 个不同的值。

【讨论】:

  • 我决定“降级”我对评论的回答(并将其修剪为唯一有用的部分)。 +1 以获得更深思熟虑的答案。 :)
  • @AndrewThompson - 感谢您的姿态,我认为我不值得(我是一个热心的浮点避免者),以及您提供的有力的严肃参考。我的是一个备忘单。
  • “备忘单” 我看重这些东西。他们可以解决大多数基本问题。 :)
  • 感谢您详细解释的回答。但是,我开始怀疑我遇到的问题可能与浮点数或双精度值的准确性无关。我在上面的问题中添加了一些说明,希望它可以指出问题的根源?再次感谢。
  • 当您说我的表达式将可靠地在 Java 中产生 false 结果时,您是对的。经过大量测试,我猜这个问题与浮动无关。不过谢谢你的回答,我从中学到了不少东西(:
【解决方案2】:

浮点数不准确。您需要指定误差范围,例如:

float result = new Float("0.00");
float expected = 0;
if (Math.abs(result - expected) < 0.00001)
...

【讨论】:

    【解决方案3】:

    您可以使用 Math.signum() 检查该值是否等于、小于或大于零。

    【讨论】:

      【解决方案4】:

      我的做法有点不同。我期待传感器显示读数为 1。这就是我想出的。

      double a = y1;
                      if((a-0)<1){
                          a=(1-a)+a;
                      }
      

      【讨论】:

        猜你喜欢
        • 2011-12-19
        • 1970-01-01
        • 2011-04-18
        • 2013-05-27
        • 2016-07-03
        • 2023-04-11
        • 2016-04-01
        • 2018-07-26
        相关资源
        最近更新 更多