Why does NaN - NaN == 0.0 with the Intel C++ Compiler?
众所周知,nan在算术上传播,但我找不到任何演示,所以我写了一个小测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <limits>
#include <cstdio>
int main(int argc, char* argv[]) {
float qNaN = std::numeric_limits<float>::quiet_NaN();
float neg = -qNaN;
float sub1 = 6.0f - qNaN;
float sub2 = qNaN - 6.0f;
float sub3 = qNaN - qNaN;
float add1 = 6.0f + qNaN;
float add2 = qNaN + qNaN;
float div1 = 6.0f / qNaN;
float div2 = qNaN / 6.0f;
float div3 = qNaN / qNaN;
float mul1 = 6.0f * qNaN;
float mul2 = qNaN * qNaN;
printf(
"neg: %f
sub: %f %f %f
add: %f %f
div: %f %f %f
mul: %f %f
",
neg, sub1,sub2,sub3, add1,add2, div1,div2,div3, mul1,mul2
);
return 0;
} |
这个例子(在这里运行)基本上产生了我所期望的(负面的有点奇怪,但它有点道理):
1 2 3 4 5
| neg: -nan
sub: nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan |
号
MSVC 2015生产了类似的产品。然而,英特尔C++ 15产生:
1 2 3 4 5
| neg: -nan(ind)
sub: nan nan 0.000000
add: nan nan
div: nan nan nan
mul: nan nan |
具体来说,qNaN - qNaN == 0.0。
这个…不可能是对的,对吧?相关标准(ISO C、ISO C++、IEEE 754)对此表示什么,为什么编译器之间的行为有差异?
- JavaScript、Python(numpy)有没有这样的行为。Nan-NaNNaN冰。Perl和Scala也表现similarly。
- 也许你unsafe基于数学优化的-ffast-math按当量(GCC)?
- "我不该说的C / C + +标准。我说,"标准"的意思,IEEE 754标准和相关的实现等在C和C + +。这是在acknowledge unclear,然而。
- "N.M.:不构成威胁。附录F,这是可选,但当规范和必要的支持,两个漂浮的行为有点specified全,essentially incorporates IEEE 754为C。
- "N.M.为写,unclear。在编辑它的现在。我想,但我不一定是754标准是这里,这样做甚至更多的将军。
- 如果你想要问(IEEE 754标准,说它的地方的问题。
- 他们说,:"很多关于专辑,事实上,在所有的地方,除非标准diverges wildly从选秀。Ctrl F C11选秀《南最终你会得到374次。
- software.intel.com /站点/默认文件/ / / /第326703 & hellip;
- "你是user2357112吧,对不起,我的记忆没有冰,
- 我的问题是关于酸性这个JavaScript从标题。
- GCC 5.1.0在Ubuntu是相同的作为你的第一个实例的输出。在没有使用任何开关,祭坛的浮点模型,所以对这一切都应该按违约。只有在使用臭氧,这不混乱与浮点模型。
- 为什么是-nanWeird?
- "我想起来就chieftwopencils均值"Weird"通用"这是一种直观的"意识",即,什么是它的均为南两个负的第一广场",而不是"侦听"unexpected大学和possibly键槽吧。"我想起来我是不知道之前这两个测试,-nan是定义在浮点数规格。
- "在我imallett '我希望你是一个最小的D agree编辑你的问题为了clarify《标准方面的问题(自顶端级冰有益但不回答你意识似乎是寻找)。
- "请不要kylestrand代用品,你自己的判断上的defining问题。他说他在"冰感兴趣的行为在两个C和C + +和你想要编辑了这两个标准,使它的一个问题。在会上你的在线编程,如果这个概念是一个标准的问题。在城市posting堆栈溢出,在实际编程Q &;一位,他是制造这一实际问题。编译器实际上表现如何,是一个实用程序和物,因此在这里有趣的话题。
- 保罗是...c / C + +的问题在本网站或是意识(C / C + +标准,即使他们不是没有标准(或他们的回答两个城市的参考标准(S)。事实上,我已经rebuffed attempting问是不是由实际行为的标准。但有两个问题,(预编辑!)说:"我不会说这是"标准"。"我没有使它的一个标准问题";它是意识世界。(一)标准的问题。只是在clarified哪些标准是相关的。
- 如果你不喜欢它,"保罗被认为是在两个事物或状态改变的意图的问题城市增的IEEE当用户只恨你的(C / C + +标准,这可能是一个合理的两个位置。而是说,问题是没有什么标准之前,我的编辑简单的冰槽的威胁。
- 德科工,我要求(标准的参与。"ouah的答案是我青睐的转矩,因为它是唯一的一个地址,事实上他们。
- 在"imallett切赫相信你会给他们第二次黄金徽章的ouah如果你接受的答案:stackoverflow.com /服务/ / /平民62徽章
- @Kylestrand的行为好奇心被排除在外,这让我非常恼火。在提供了一些早期广受欢迎的见解之后,新读者抱怨我没有回答这个问题。当然,事实上的工作标准(大多数开发人员通常使用的实际编译器)和标准文档在代码中都很重要,在这段时间内,如果双方都同意并且有先例表明未来的开发应该使它们保持一致,那么它们可能是安全的。
- @Kylestrand对于您之前的问题,没有人回答是很遗憾的,但是要从零开始回答这个问题需要很多工作,因为它需要一个测试套件,但是可以用一个测试套件和一些Docker容器来完成多个版本等。
- @保罗啊,我没有预料到对现有答案会产生负面影响,因为在我的头脑中,我保留了问题的最初意图。这更有道理。我不认为我把插入语仅仅解释为对"标准"所述问题的澄清是不合理的,但是你是对的,我的编辑删除了所有关于"行为"的提及。我认为如果它将要出现在所有,它不应该在插入语之内。我又做了一次编辑。
- op,当前版本可以接受吗?
- @保罗,没有人回答我的问题,我比我更不恼火,因为我拒绝了这样一个想法,即答案首先可能是有价值的。
- @Kylestrand是;当前版本可以接受。我最初的问题是关于标准的。由于原因主要是编译器,所以我认为最好让答案同时解决这两个问题。
- 您可以在这里添加一些volatile。
- Negative Nan的奇怪之处在于,neg < 0是false:melpon.org/wandbox/permlink/o8jwn6rwomome9wy
英特尔C++编译器中的默认浮点处理是EDOCX1×0,它不安全地处理EDCOX1×1的引用(这也导致EDCOX1×2是EDCOX1×3)。尝试指定/fp:strict或/fp:precise,看看是否有帮助。
- 我只是自己试一下。事实上,指定精确或严格可以解决问题。
- 我想赞同英特尔对/fp:fast违约的决定:如果你想要安全的东西,你最好首先避免出现nan,一般不要使用带浮点数的==。依靠IEEE754分配给Nan的奇怪语义,这是在自找麻烦。
- @左邻右舍:你觉得南有什么奇怪的地方,除了有南这个可怕的决定!=nan返回真?
- nan有重要的用途-它们可以检测异常情况,而无需在每次计算后进行测试。不是每个浮点开发人员都需要它们,但不要忽略它们。
- @关于快速与安全的选择,左邻右舍与fdiv bug的处理是一致的:一般用户很少遇到这种情况,所以选择最快的解决方案。
- @超级卫星出于好奇,你同意让NaN==NaN返回false的决定吗?
- @凯尔斯特兰:不,但如果是南!=nan返回了true,代码至少可以使用反向逻辑!防止一些问题发生。真正需要的(由于一些奇怪的原因,目前还不普遍可用)是执行完全有序关系比较的标准化、有效的方法。虽然有时让NaN无序是有用的,但也有其他一些时候,例如当试图排序一个数字列表时,它会对所有的事情造成破坏。如果我在设计一种语言,我会有多种类型的浮点和双浮点,它们是可变的…
- …但是使用了不同的NaN比较语义(只允许在具有相同语义的值之间进行操作,除非非NaN常量可以自由转换为任何其他形式);我希望"通用"类型使用完全排名的运算符。
- @超级跑车啊。我喜欢那样。
- @超混沌是科学计算中保持不定形式正确语义的重要手段。我猜这个用例比包含nan的排序列表的效率更常见/更重要。
- @Leftaroundabout:你的立场是,如果我想要"安全"的东西,我最好不要使用任何帮助我编写快速和安全的代码的功能,因为你认为它们很奇怪?
- @supercat如果你在排序一个包含NaN的数字列表,你会把它放到正无穷大还是负无穷大?(而且,仅仅对列表进行预处理以删除所有nan是否足够简单?)
- @tmyklebu:不,你最好使用其他的功能来帮助你编写快速和安全的代码。特别是,类型系统可以保证的任何内容都比运行时检查(无论是通过NAN还是不通过NAN)更好。C++模板为您提供了很多可能性,Hindley Milner类型的系统如OcAML或Haskell甚至更好。
- @用户168715:这两种形式在不同的时间都是需要的,但是(1)即使在科学计算中,我也希望有一个将NaN视为不等于任何事物(包括其他NaN)的运算符与将其视为不等于任何事物的运算符一样重要,因此我不理解使NaN返回为eve的错误的基础。Rything but不等式,和(2)IEEE应该从一开始就非常明确地指出,实现应该提供一种完全有序的比较方法和一种"科学"的比较方法,而且它还提供计算的函数…
- …以圆或象限为单位的三角函数。虽然有一些用途,弧度是自然单位,但也有同样多的计算以π因子结束,如果将圆用作单位,则可以忽略该因子。考虑到许多正弦和余弦实现无论如何都必须转换为一个圆的两个分数的幂,要求客户机代码乘以pi,库代码乘以1/pi并不是高精度的方法。
- 的BTW:@,也user168715 should have been there)四:零(零- ISH值加或减的东西本身,收益率为零)的正面和负面的infinitesimal(result of底流分倍频或在线(Unsigned),和infinitesimal result of减产品(零)。在signed给分模式infinitesimal should + / - INF模式或零分,但infinitesimal Unsigned should是南。这样的安排会让安在properly对称操作之间的正面和负面的数字。
- 你所写的:"我leftaroundabout数字代码?系统将不保存你的幻想型。是我的方式nans handled carefully设计这样的错误,但仍然check for You can have to pollute *你的尾巴与显conditionals taken,rarely -到处都是。
- 2008年IEEE 754 supercat @(或:你不管一个人多punctuate EN)都在提供implementations that does recommend and the total阶浮点比较像往常一样。不幸的是他/她,他们是能够dictate T to the syntax c和c++的委员会。恩我也和朋友sinpirecommends cospi投诉你的第二职业。的同意,虽然它不是);t there at the beginning。
- 在sinpi烯丙基(but not to等传统replacing)is the versions弧度结束的想法但unrelated how to the question of handled nans should be。
- tmyklebu @工作:网络技术和therefore MHD代码,nans Do not for sure that甚efficiently"拯救你"。大学学位,当他们弹出,你可以generally things have"措施,好漂亮的一awry,but not as a本身误差manifests南*在所有。他们甚至没有yet,经常会有好的和efficiently -- checkable is wrong -提示的东西(例如,消极的密度)。这些提示是——但不甚of for the error帮助调试,因为我在很好的*大usually section of队列。for that…
- ...kind of调试,你需要把你的本地检查–在线低效给梅好你真的which as for a快速检查-啊-饥饿处理每个生产运行。but if that is most甚至比编译器错误计数模式是好茶,是在强型和帮助系统。当然他们不find every of error,毋宁,but they can find a Heck of a Lot of errors。admittedly not yet with such挤压我的C + +代码11,ocaml of Haskell to confirm恩;但缺乏这样的代码是ascribe of the to学术惯性。
- 结果同意与tmyklebu @ leftaroundabout that is here inference useless型。(几乎)在编译时没有人nans writes to下溢:他们两个arise over the of many /溢出)的计算可以预测复合iterations of them and running the program is rarely easier比本身。仿真实例考虑for the of a三体问题:if any of the三双为each other太靠近身体,nans arise(Will to the队在两个缓冲区溢出的比例1 / R ^ 2 $ $)but of the道经典的三体问题is the example of that cannot在混沌系统好analyzed在编译时间。
- "嗯,那是在user168715 example:在T型系统不会帮助,因为它不是真的在"the程序bug,茶叶中的问题(为什么我的设置是可能的缓冲区溢出-克洛斯,诱导在第一附近的地方?)
- 但不幸的是他/她对leftaroundabout @这是nans arise在实践知识。可以想象,在一个大型队列需要数值积分步of number of time for the三体问题(for efficiency)和periodically for nans和其他问题的检查,如果detected,rewinds Time(时间偏差在杰弗逊的S)和那些与reruns iterations(更多的特殊处理,把手体collisions’)。
- user168715 @:对,但你不需要我的移调- nans to that of级数"给你。在高能源impossibly动力学检查结果将是一样多的。
- @tmyklebu:我知道IEEE在2008年就开始推荐这样的东西了,但是有人对此采取了行动吗?打破默认的相等关系,而不是用一个函数来测试两个变量是否"科学地"相等,这使得一种语言几乎不可能支持ieee语义,同时也规定==运算符将为其编译的操作数组合定义一个等价关系[如果我被设计为在语言方面,我将包括"ieee32"和"ieee64"两种类型,它们具有.ieee_eq()方法,但不允许==运算符,并且具有==测试等效性]。
- @左邻右舍:我从来没有过一个花哨的系统会捕捉到的数字booboo。(但我也从未从事过任何与物理学有关的工作。我做优化,我们没有单位。)Nan传播让您为常见情况编写清晰的逻辑,并可以证明检测出是否有什么地方出错。检查"不可能的高动能"通常可以在物理模拟中工作,但有可能设计出一个失败的输入。
- @supercat:一些奇特的数学库(如crlibm)具有trigpi函数。如果你需要在浮点数上使用<,这两者都可以在浮点数上使用<,并且可以作为一个总顺序使用,那么你就处于深深的罪恶之中。
- @tmyklebu:很多情况下浮点算法可以从记忆化中受益。如果f(x)很昂贵,而且它的输出只依赖于x,那么保存一个x值列表和调用f(x)的结果通常是一个有用的优化,但我知道的唯一方法需要一种检查两个浮点值是否相等的方法,以及我知道的唯一有效的方法。有时需要散列或排序浮点值的方法。记忆化是否晦涩难懂,或者没有上述特征,有没有有效的方法?
- 记忆化并不模糊,但也不需要完全排序。通过memcpy或类似的方法将double转换为等大小的整数类型,如int64_t,并使用该int64_t作为密钥。(或者,如果你是这些人中的一员,也可以叫array。)
- 作为一个旁注,英特尔C++编译器具有(现在不赞成/FP和默认OFF)/OP选项,该文档描述为:"使符合ANSI C和IEEE 754标准的浮点运算"。并且"指定/op选项对程序编译有以下影响:……]浮点算术比较符合IEEE 754规范,NAN行为除外。"文档没有详细介绍/fp选项的优化/不一致性。
- @tmyklebu:有很多方法可以从c中的double获得总的订单,但是涉及memcpy甚至union的黑客最终会迫使额外的寄存器->内存往返。此外,我认为IEEE-754的实用性不应该局限于支持这种位级黑客的语言。
- 我们能不能都同意浮点数通过否定来证明上帝的存在,因为它们显然是魔鬼的工作?-)
- @超级跑车:你看过铁锈吗?它定义了4个可供比较的特征(接口):PartialEq、PartialOrd、Eq和Ord四个特征(接口),后两个特征(接口)是你从==、!=、<、<=、>和>=的厂外供应商(f32和f64做的浮点类型(f32和f64做的浮点类型(EDOCX11〔29〕和EDOCX11〔30〕做的)4个特征(接口),但浮点类型(EDOCX11〔29〕和EDOCX11〔30〕做的)浮点类型(edoc不执行Eq和Ord,但只有PartialEq和PartialOrd:doc.rust-lang.org/std/cmp/trait.partialord.html
- @Matthieum:我对Rust做了很简短的研究;我喜欢"let"和"let mut"之间的概念性区别,但对它知之甚少。如何使用部分排序,如果需要对reals(NaN映射为"高"或"低")进行总排序,该怎么办?
- @supercat:您可以通过调用特性的方法来使用部分排序:fn partial_cmp(&self, &other) -> Option返回无排序或排序;对于浮点,与NaN比较不会产生排序。如果要定义总排序,可以创建一个新类型:struct MyF32(f32);,然后可以为您的类型实现接口,并决定如何进行比较。主要的好处是它迫使人们明确地选择对他们有意义的行为。
- @Matthieum:我不是想问人们用什么方式问物体部分排序,而是问人们如何有效地使用它。是否有任何有用的通用算法需要任何不需要完全排序的排序?
- @超级卫星:说实话?我不知道!
- 说到英特尔的fp:fast默认值,它大致匹配gcc-ffast-math-fno-cx限制范围,而fp:fast=2包括-复杂的限制范围等等。我是少数人之一,他们更喜欢突然下溢和K&R风格的忽略括号不包括在这些选项中。我还保留了在这些选项下处理倒易数学的方式。但我大部分时间都在使用它们。
这个。…不可能是对的,对吧?我的问题是:相关标准(ISO C、ISO C++、IEEE 754)对此有何看法?
PetrAbdulin已经回答了为什么编译器给出了0.0的答案。
以下是IEEE-754:2008所说的:
(6.2 Operations with NaNs)"[...] For an operation with quiet NaN inputs, other than maximum and minimum operations, if a floating-point result is to be delivered the result shall be a quiet NaN which should be one of the input NaNs."
号
因此,两个安静NaN操作数的减法的唯一有效结果是一个安静NaN;任何其他结果都无效。
C标准规定:
(C11, F.9.2 Expression transformations p1)"[...]
x ? x → 0. 0"The expressions x ? x and 0. 0 are not equivalent if x is a NaN or
infinite"
号
(此处,NaN表示F.2.1p1中的安静NaN,"本规范未定义信号NaN的行为。它通常使用术语nan表示安静的nan")。
因为我看到了一个指责英特尔编译器标准遵从性的答案,而没有其他人提到过这一点,我将指出GCC和Clang都有一种模式,在这种模式下,它们可以做一些非常相似的事情。它们的默认行为符合IEEE-
1 2 3 4 5 6 7 8 9 10 11 12 13
| $ g++ -O2 test.cc && ./a.out
neg: -nan
sub: nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan
$ clang++ -O2 test.cc && ./a.out
neg: -nan
sub: -nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan |
-但是如果你以牺牲正确性为代价要求速度,你就会得到你所要求的-
1 2 3 4 5 6 7 8 9 10 11 12 13
| $ g++ -O2 -ffast-math test.cc && ./a.out
neg: -nan
sub: nan nan 0.000000
add: nan nan
div: nan nan 1.000000
mul: nan nan
$ clang++ -O2 -ffast-math test.cc && ./a.out
neg: -nan
sub: -nan nan 0.000000
add: nan nan
div: nan nan nan
mul: nan nan |
号
我认为批评ICC的违约选择是完全公平的,但我不会把整个Unix战争重新解读成这个决定。
- 注意,对于-ffast-math,gcc不再符合关于浮点运算的ISO 9899:2011。
- @是的,要点是两个编译器都有一个不兼容的浮点模式,只是ICC默认为该模式,而GCC没有。
- 只是为了给自己添点油,我真的很喜欢英特尔默认的快速数学。使用浮动的关键是获得高吞吐量。