为什么与NaN不同,为什么浮点数无穷大?
为什么无限比较遵循适用于NaN的逻辑? 此代码打印出三次false
:
double a = Double.NaN; double b = Double.NaN; System.out.println(a == b); // false System.out.println(a < b); // false System.out.println(a > b); // false
但是,如果将Double.NaN
更改为Double.POSITIVE_INFINITY
,则对于等于或小于比较,我会得到相等的结果:
double a = Double.POSITIVE_INFINITY; double b = Double.POSITIVE_INFINITY; System.out.println(a == b); // true System.out.println(a < b); // false System.out.println(a > b); // false
这似乎很危险。 假设无限值是由溢出产生的,那么我认为在完美算术中,两个以infinities结尾的variables实际上是不相等的。
你的推理是Double.POSITIVE_INFINITY
不应该等于自己,因为它可能是由于精度的损失而获得的。
这条推理适用于所有的浮点。 任何有限的值都可以作为不准确的操作的结果而获得。 这并没有促使IEEE 754标准化委员会将==
定义为对于有限值总是进行假定,那么为什么无穷大应该是不同的呢?
正如所定义的那样, ==
对于理解它的function的用户是有用的(即, testing已经获得的浮点值,当然不是通过实际计算得到的值)。 对于任何懂得这一点的人来说,即使对于不涉及无穷大的计算,也需要理解它,以便使用浮点, Double.POSITIVE_INFINITY == Double.POSITIVE_INFINITY
求值为true方便,如果仅testing浮点浮点运算的结果是Double.POSITIVE_INFINITY
。
这就产生了NaN为什么会有特殊行为的问题,而且无限性应该遵循与有限值相同的一般原则。 NaN与infinities不同:IEEE 754标准的基本原理是数值正是它们的样子,但操作的结果可以近似于实际的结果,在这种情况下,得到的浮点值是根据舍入模式获得的。
忘记1.0 / 0.0
被定义为+ inf的瞬间,这在这个讨论中是一个烦恼。 考虑Double.POSITIVE_INFINITY
的时刻只能作为诸如1.0e100 / 1.0e-300
或Double.MAX_VALUE + Double.MAX_VALUE
等操作的结果。 对于这些操作,+ inf是实际结果的最接近的近似值,就像产生有限结果的操作一样。 相比之下,NaN是手术没有意义时获得的结果。 让NaN有特殊的行为是可以的,但是inf只是所有值太大而不能代表的近似值。
实际上, 1.0 / 0.0
也会产生+ inf,但这应该被认为是一个例外。 将该操作的结果定义为NaN一样一致,但将其定义为+ inf在实现某些algorithm时更为方便。 Kahan的笔记第10页提供了一个例子。 更多的细节比大多数人希望的是在文章“复杂的基本function的分支,或者很多关于什么的符号位” 。 我也将解释在IEEE 754中存在一个与NaN标志分开的“除零”标志,作为用户可能想特别处理除零的认识,虽然它没有被定义为产生NaN。
因为那是标准。 Infinity表示一个大于或小于Double.MAX_VALUE / -Double.MAX_VALUE的数字。
NaN代表一个没有意义的操作的结果。 也就是说,这个手术不可能出来一个号码。
我想逻辑是一旦一个数字变得足够大(无穷大),并且由于浮点数的限制,向它添加数字不会改变结果,所以它就像'无限'一样。
所以,如果你想比较真正的大数字,在某些时候,你可能会说这两个大数字已经足够接近所有的意图和目的。 但是如果你想比较两个都不是数字的东西,你就不能比较它们的错误。 至less你不能把它们比作原始的。
为什么无穷大是平等的? 因为它工作。
浮点运算被devise为产生(相对)快速的计算以保存错误。 这个想法是,在冗长的计算过程中,你不会检查溢出或其他的废话。 你等到它完成。 这就是为什么NaNs以他们的方式传播:一旦你获得了NaN,那么你可以做的事情很less,这会让它消失。 一旦计算完成,您可以查找NaN来检查是否出错。
无穷无尽:如果有溢出的可能性,不要做会抛弃无穷的事情。
如果你想慢慢安全的话,IEEE-754有机制安装陷阱处理程序,当计算结果是NaN或无穷时,为你的代码提供callback。 大多数情况下没有使用; 一旦代码被正确debugging,通常太慢,毫无意义(这不是那么容易:人们得到博士学习如何做到这一点)。
certificate“无限”值相等的另一个观点是完全避免基数概念。 本质上,如果你不能推测“一个数值与另一个数值的比较是多么的无限”,那么假设Inf = Inf
,这是比较简单的。
编辑:作为关于基数的评论的澄清,我将给出两个关于无限量比较(或相等)的例子。
考虑一组正整数S1 = {1,2,3, ...}
,这是无限的。 还要考虑一组偶数整数S2 = {2,4,6, ...}
,这也是无限的。 虽然S1中的元素明显多于S2中的元素,但它们具有“相同数量”的元素,因为您可以轻松地在集合之间具有一对一的function,即1 -> 2
2-> 4
。他们有相同的基数。
而是考虑一组实数R
和一组整数I
再次都是无限的集合。 然而,对于每个整数i
,在(i, i+1)
之间存在无限多的实数。 因此,没有一对一的function可以映射这两个集合的元素,因此它们的基数是不同的。
底线: 无限量的平等是复杂的,在命令式语言中更容易避免它 🙂
对我而言,似乎“因为它应该performance为零”将是一个很好的答案。 算术溢出和下溢应该类似地可操作。
如果从可以存储在浮点数中的最大近似无限小的值下溢,则得到零,并且将零比较为相同。
如果您从可以存储在浮点数中的最大近似无穷大值溢出,则会得到INF,并将INF比较为相同。
这意味着处理在两个方向超出范围的数字的代码不需要为一个或另一个单独的特殊套pipe。 相反,两者或两者都不需要区别对待。
最简单的要求是“既不是”的情况:你想检查是否超出/下溢,你可以使用普通的算术比较运算符来比较零/ INF,而不需要知道当前语言的特殊语法检查命令:它是Math.isInfinite(),Float.checkForPositiveInfinity(),hasOverflowed()…?
正确的答案是一个简单的“因为标准 (和文档 )这样说”。 但我不会玩世不恭,因为很明显,这不是你所追求的。
除了这里的其他答案之外,我会尝试将无穷大与饱和算术联系起来。
其他答案已经表明,NaNs比较的结果是true
,所以我不会击败一匹死马。
假设我有一个表示灰度色彩的饱和整数。 为什么我使用饱和算术? 因为比白色还亮的东西还是白色的,比黑色的东西还黑的( 橙色的除外)。 这意味着BLACK - x == BLACK
和WHITE + x == WHITE
。 说得通?
现在,我们假设我们想用一个(带符号的) 补码 8位整数来表示那些灰度色,其中BLACK == -127
和WHITE == 127
。 为什么1s补全? 因为它给了我们像IEEE 754浮点一样的有符号零 。 而且,因为我们使用饱和算术, -127 - x == -127
和127 + x == 127
。
这与浮点数无关有什么关系? 用浮点数replace整数,用NEGATIVE_INFINITY
replaceBLACK
,用POSITIVE_INFINITY
replaceNEGATIVE_INFINITY
,你会得到什么? NEGATIVE_INFINITY - x == NEGATIVE_INFINITY
和POSITIVE_INFINITY + x == POSITIVE_INFINITY
。
既然你使用了POSITIVE_INFINITY
,我也会使用它。 首先,我们需要一个类来表示我们饱和的基于整数的颜色; 让我们将其称为SaturatedColor
并假定它像Java中的任何其他整数一样工作。 现在,让我们把你的代码和SaturatedColor
和Double.POSITIVE_INFINITY
用SaturatedColor.WHITE
replace为double
:
SaturatedColor a = SaturatedColor.WHITE; SaturatedColor b = SaturatedColor.WHITE;
正如我们上面所build立的, SaturatedColor.WHITE
(上面的WHITE
)是127
,所以让我们这样做:
SaturatedColor a = 127; SaturatedColor b = 127;
现在我们用你使用的System.out.println
语句,并用它们的值(值?)replacea
和b
:
System.out.println(127 == 127); System.out.println(127 < 127); System.out.println(127 > 127);
这应该是显而易见的,这将打印。
由于提到了Double.Nan.equals(Double.NaN):当您执行算术和比较数字时,发生什么事情是一回事,当您考虑对象的行为时,这是完全不同的事情。
两个典型的问题是:对数组进行sorting,并使用散列值实现字典,集合等等。 有两种例外的情况,其中<,=和>的正常顺序不适用:一种情况是+0 = -0,另一种情况是NaN≠NaN,x <NaN,x> NaN,x = NaN不pipex是什么,总会是假的。
sortingalgorithm可能会遇到麻烦。 sortingalgorithm可以假设x = x总是为真。 所以,如果我知道x存储在一个数组中并寻找它,我可能不会做任何边界检查,因为它的search必须find一些东西。 如果x是NaN,则不是。 sortingalgorithm可以假设<b和a> = b中的一个必须为真。 不是如果一个是NaN。 所以当NaN存在时,一个天真的sortingalgorithm可能会崩溃。 您必须决定在sortingarrays时最终需要NaN的位置,然后更改您的比较代码,以便它能够正常工作。
现在字典和集合,一般哈希:如果我使用NaN作为关键? 一个集合包含独特的对象。 如果这个集合包含一个NaN,我试图添加另外一个,那它是不是唯一的,因为它不等于那个已经存在的那个? 那么+ 0和-0呢,他们应该被认为是相同还是不同呢? 有任何两个项目被认为相等的规则必须具有相同的散列值。 所以明智的事情是(可能)哈希函数为所有NaN返回一个唯一值,为+0和-0返回一个唯一值。 并且在哈希查找之后,当您需要查找具有相同哈希值的元素时,应该将两个NaN视为相等(但不同于其他)。
这可能是为什么Double.Nan.equal()performance与==不同的原因。
这是因为NaN不是一个数字,因此不等于包括NaN在内的任何数字。