草庐IT

c++ - std::isinf不适用于-ffast-math。如何检查无穷大

coder 2024-02-07 原文

样例代码:

#include <iostream>
#include <cmath>
#include <stdint.h>

using namespace std;

static bool my_isnan(double val) {
    union { double f; uint64_t x; } u = { val };
    return (u.x << 1) > 0x7ff0000000000000u;
}

int main() {
    cout << std::isinf(std::log(0.0)) << endl;
    cout << std::isnan(std::sqrt(-1.0)) << endl;
    cout << my_isnan(std::sqrt(-1.0)) << endl;
    cout << __isnan(std::sqrt(-1.0)) << endl;

    return 0;
}

Online compiler

使用-ffast-math,该代码显示“0,0,1,1”-不显示,则显示“1,1,1,1”。

那是对的吗?我认为在这种情况下,std::isinf/std::isnan应该仍然可以与-ffast-math一起使用。

另外,如何使用-ffast-math检查infinity/NaN?您可以看到my_isnan确实在执行此操作,但是该解决方案当然取决于体系结构。另外,为什么my_isnan在这里工作而std::isnan不起作用?那__isnan__isinf呢?他们总是工作吗?

使用-ffast-mathstd::sqrt(-1.0)std::log(0.0)的结果是什么。它会变得不确定,还是应该是NaN/-Inf?

相关讨论:(GCC) [Bug libstdc++/50724] New: isnan broken by -ffinite-math-only in g++(Mozilla) Bug 416287 - performance improvement opportunity with isNaN

最佳答案

请注意,-ffast-math可能会使编译器忽略/违反IEEE规范,请参阅http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Optimize-Options.html#Optimize-Options:

This option is not turned on by any -O option besides -Ofast since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions. It may, however, yield faster code for programs that do not require the guarantees of these specifications.



因此,使用-ffast-math并不能保证在应有的位置看到无穷大。

特别地,-ffast-math打开-ffinite-math-only,请参阅http://gcc.gnu.org/wiki/FloatingPointMath,这意味着(来自http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Optimize-Options.html#Optimize-Options)

[...] optimizations for floating-point arithmetic that assume that arguments and results are not NaNs or +-Infs



这意味着,通过启用-ffast-math,您可以向编译器保证代码将永远不会使用无穷大或NaN,这反过来又允许编译器通过例如用常量isinf替换对isnanfalse的任何调用来优化代码。并从那里进一步优化)。如果您违背了对编译器的 promise ,则不需要编译器来创建正确的程序。

因此,答案很简单,如果您的代码可能具有无限性或NaN(这是您使用isinfisnan的事实所强烈暗示的),则您无法启用-ffast-math,否则可能会得到不正确的代码。
my_isnan的实现可以工作(在某些系统上),因为它直接检查浮点数的二进制表示形式。当然,处理器仍可能进行(某些)实际计算(取决于编译器进行的优化),因此实际的NaN可能会出现在内存中,您可以检查其二进制表示形式,但是如上所述,std::isnan可能已被替换为常量false。同样可能发生的是,编译器用某种甚至不会为输入sqrt生成NaN的版本替换-1等。为了查看您的编译器进行了哪些优化,请编译为汇编器并查看该代码。

为了进行类比(并非完全不相关),如果您告诉编译器您的代码是C++,则不能指望它正确地编译C代码,反之亦然(有实际的示例,例如Can code that is valid in both C and C++ produce different behavior when compiled in each language?)。

启用-ffast-math并使用my_isnan是一个坏主意,因为这会使所有事情都非常依赖于计算机和编译器,您不知道编译器总体上会进行哪些优化,因此可能存在与您使用的事实有关的其他隐藏问题非限制性数学,但告诉编译器否则。

一个简单的解决方法是使用-ffast-math -fno-finite-math-only,它仍然会提供一些优化。

也可能是您的代码看起来像这样:
  • 过滤掉所有无穷和NaN
  • 对过滤后的值进行一些有限的数学运算(这是指保证永远不会创建无限或NaN的数学运算,必须非常非常仔细地检查它)

    在这种情况下,您可以拆分代码,并使用optimize #pragma__attribute__选择性地为给定的代码段打开和关闭-ffast-math(分别为-ffinite-math-only-fno-finite-math-only)(但是,我记得某些版本的GCC出现了问题与此相关)或只是将您的代码拆分为单独的文件,然后使用不同的标志进行编译。当然,如果您可以隔离可能出现无限性和NaN的零件,那么这在更常规的设置中也适用。如果您无法隔离这些部分,则表明您无法将此代码使用-ffinite-math-only

    最后,重要的是要了解-ffast-math不是无害的优化,它只会使您的程序更快。它不仅会影响代码的性能,而且还会影响其正确性(而且,这已经是围绕浮点数的所有问题了,如果我还记得正确的William Kahan在他的主页上有一系列恐怖故事,另请参阅What every programmer should know about floating point arithmetic)。简而言之,您可能会获得更快的代码,但也会得到错误或意外的结果(请参见下面的示例)。因此,仅当您真正知道自己在做什么并且已经完全确定时,才应使用此类优化
  • 优化不会影响该特定代码或
  • 的正确性
  • 优化引入的错误对于代码并不重要。

  • 取决于是否使用此优化,程序代码的行为实际上可能完全不同。尤其是在启用诸如-ffast-math之类的优化后,它的行为可能会出错(或者至少与您的期望非常相反)。以以下程序为例:
    #include <iostream>
    #include <limits>
    
    int main() {
      double d = 1.0;
      double max = std::numeric_limits<double>::max();
      d /= max;
      d *= max;
      std::cout << d << std::endl;
      return 0;
    }
    

    编译时不带任何优化标志时,将按预期产生输出1,但是使用-ffast-math,它将输出0

    关于c++ - std::isinf不适用于-ffast-math。如何检查无穷大,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22931147/

    有关c++ - std::isinf不适用于-ffast-math。如何检查无穷大的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

      关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

    4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

      给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

    5. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    6. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

      大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

    7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

      我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

    8. ruby - 如何指定 Rack 处理程序 - 2

      Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

    9. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

      在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

    10. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

      我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

    随机推荐