我最近读了很多关于IEEE 754和x87架构的内容.我正在考虑在我正在研究的一些数值计算代码中使用NaN作为"缺失值",我希望使用信令 NaN将允许我在我不想要的情况下捕获浮点异常继续"缺失值".相反,我会使用安静的 NaN来允许"缺失值"通过计算传播.但是,信号NaN不起作用,因为我认为它们将基于它们上存在的(非常有限的)文档.
以下是我所知道的摘要(所有这些都使用x87和VC++):
标准库提供了一种访问NaN值的方法:
std::numeric_limits<double>::signaling_NaN();
Run Code Online (Sandbox Code Playgroud)
和
std::numeric_limits<double>::quiet_NaN();
Run Code Online (Sandbox Code Playgroud)
问题是我认为信号NaN没有任何用处.如果屏蔽了_EM_INVALID,则其行为与安静NaN完全相同.由于没有NaN与任何其他NaN相当,因此没有逻辑差异.
如果未屏蔽_EM_INVALID (启用异常),则甚至无法使用信号NaN初始化变量:
double dVal = std::numeric_limits<double>::signaling_NaN();因为这会引发异常(信号NaN值被加载到x87寄存器以将其存储到存储器地址).
您可以像我一样思考以下内容:
但是,步骤2会导致信令NaN转换为安静的NaN,因此后续使用它不会导致异常被抛出!那么WTF?!
信号NaN是否有任何实用性或目的?我理解其中一个原始意图是使用它初始化内存,以便可以捕获使用单位化浮点值.
有人能告诉我,如果我在这里遗失了什么吗?
编辑:
为了进一步说明我希望做的事情,这里有一个例子:
考虑对数据向量(双精度)执行数学运算.对于某些操作,我想允许向量包含"缺失值"(假设这对应于电子表格列,例如,其中一些单元格没有值,但它们的存在很重要).对于某些操作,我不希望允许向量包含"缺失值".如果集合中存在"缺失值",也许我想采取不同的行动 - 可能执行不同的操作(因此这不是无效的状态).
这个原始代码看起来像这样:
const double MISSING_VALUE = 1.3579246e123;
using std::vector;
vector<double> missingAllowed(1000000, MISSING_VALUE);
vector<double> missingNotAllowed(1000000, MISSING_VALUE);
// ... populate missingAllowed and missingNotAllowed with (user) data...
for (vector<double>::iterator it = missingAllowed.begin(); it != missingAllowed.end(); ++it) …Run Code Online (Sandbox Code Playgroud) 我今天正在阅读研究人员发现NVidia的Phys-X库使用x87 FP与SSE2.显然,对于速度超过精度的并行数据集来说,这将是次优的.然而,文章作者继续引用:
英特尔在2000年末引入P4后开始不鼓励使用x87.AMD自2003年K8以来已弃用x87,因为x86-64定义为SSE2支持; VIA的C7自2005年以来一直支持SSE2.在64位版本的Windows中,x87不适用于用户模式,完全禁止在内核模式下使用.自2005年以来,业内所有人都推荐SSE超过x87,除非软件必须在嵌入式Pentium或486上运行,否则没有理由使用x87.
我想知道这件事.我知道x87内部使用80位扩展双精度值来计算值,而SSE2则不然.这对任何人都没关系吗?这对我来说似乎很惊讶.我知道当我对平面中的点,线和多边形进行计算时,在进行减法时,值可能出乎意料地错误,并且由于缺乏精度,区域可能会折叠并且线条会相互别名.我想,使用80位值与64位值可能会有所帮助.
这是不正确的?如果没有,如果x87被淘汰,我们可以用什么来执行扩展的双FP操作?
我知道x87具有更高的内部精度,这可能是人们在它与SSE操作之间看到的最大差异.但我不得不怀疑,使用x87还有其他好处吗?我有-mfpmath=sse在任何项目中自动输入的习惯,我想知道我是否遗漏了x87 FPU提供的任何其他内容.
我有一个奇怪的内存损坏问题.经过几个小时的调试和尝试,我想我找到了一些东西.
例如:我做一个简单的字符串赋值:
sTest := 'SET LOCK_TIMEOUT ';
Run Code Online (Sandbox Code Playgroud)
但是,结果有时会变成:
sTest = 'SET LOCK'#0'TIMEOUT '
Run Code Online (Sandbox Code Playgroud)
所以,_被0字节取代.
我在System.Move函数中看到过这种情况发生一次(复制很棘手,取决于时间),当它使用FPU堆栈(fild,fistp)进行快速内存复制时(如果要移动9到32个字节):
...
@@SmallMove: {9..32 Byte Move}
fild qword ptr [eax+ecx] {Load Last 8}
fild qword ptr [eax] {Load First 8}
cmp ecx, 8
jle @@Small16
fild qword ptr [eax+8] {Load Second 8}
cmp ecx, 16
jle @@Small24
fild qword ptr [eax+16] {Load Third 8}
fistp qword ptr [edx+16] {Save Third 8}
...
Run Code Online (Sandbox Code Playgroud)
使用FPU视图和2个内存调试视图(Delphi - > View - > Debug - > CPU - > Memory)我看到它出错...一次......无法重现......
今天早上我读到了关于8087CW模式的一些内容,是的,如果将其更改为$ …
最近我在使用FPU堆栈溢出时遇到了一些麻烦.我设法将它追溯到一个错误的库函数,每次调用它时都会将垃圾值推送到FPU堆栈,并且永远不会清理它.
幸运的是,这很容易重现,我确切地知道它导致了什么条件.我可以将一行内联ASM放入调用此例程的例程中,将最高值从FPU堆栈中弹回...除了我不太清楚要写什么.我的ASM-fu对于middlin来说是公平的,但不是那么强大.
那么在x86汇编中摆脱FPU堆栈顶部值的最简单方法是什么,假设它是垃圾数据并且我不关心它的值?
x87 FPU值得注意的是使用内部80位精度模式,这通常会导致编译器和机器出现意外和不可重现的结果.在我搜索 .NET上可重现的浮点数学时,我发现.NET(Microsoft和Mono)的两个主要实现都在64位模式下发出SSE指令而不是x87.
SSE(2)严格使用32位寄存器用于32位浮点数,严格使用64位寄存器用于64位浮点数.通过设置适当的控制字,可以选择将非正规数刷新为零.
因此,似乎SSE不会受到x87的精度相关问题的影响,并且唯一的变量是可以控制的非正规行为.
抛开超越函数的问题(SSE本身不像x87那样提供),是否使用SSE保证了机器和编译器之间可重现的结果?例如,编译器优化会转化为不同的结果吗?我发现了一些相互矛盾的观点:
如果您有SSE2,请使用它并从此过上幸福的生活.SSE2支持32b和64b操作,中间结果具有操作数的大小.- Yossi Kreinin,http://www.yosefk.com/blog/consistency-how-to-defeat-the-purpose-of-ieee-floating-point.html
...
SSE2指令(...)完全符合IEEE754-1985标准,它们具有更好的可重复性(由于静态舍入精度)和其他平台的可移植性.Muller et aliis, Handbook of Floating-Point Arithmetic - p.107
然而:
此外,您不能将SSE或SSE2用于浮点,因为它太低于指定而不具有确定性.- John Watte http://www.gamedev.net/topic/499435-floating-point-determinism/#entry4259411
我有一个Qt C++应用程序,其中有一个GUI线程,其中发生一些浮点计算.它还会打开QWebView一个带有一些视频的flash播放器.
很明显,关闭QWebView会干扰新的下一个浮点运算.因此pow(double, double)返回明确但不正确的值.
在一种情况下,它返回的值1000多于正确的值.另一次它返回1. #inf与参数一起使用时pow(10.0, 2.0).
我必须提到它是在不同的计算机上测试的,并不是特定于特定的CPU.
您是否有任何关于如何在Webkit中找到协处理器出错的地方以及如何防止它的建议?
环境:Qt 4.7.4,C++,HTML和flowplayer
CPP
wrongpow::wrongpow(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
QVBoxLayout* layout = new QVBoxLayout(0);
m_view = new QWebView(this);
m_view->setMinimumSize(400, 400);
m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
m_view->settings()->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
layout->addWidget(m_view);
QDir dir(QApplication::applicationDirPath());
dir.cd("media");
m_view->load(QUrl(QFileInfo(dir, "index.html").absoluteFilePath()));
QPushButton* button = new QPushButton(QLatin1String("Click on video start"), this);
layout->addWidget(button);
Q_ASSERT(connect(button, SIGNAL(clicked()), this, SLOT(closeView())));
setLayout(layout);
adjustSize();
}
Q_SLOT void wrongpow::closeView()
{
delete m_view;
m_view = NULL;
double wrongResult = …Run Code Online (Sandbox Code Playgroud) 我正在研究用LLVM编译的语言.只是为了好玩,我想做一些微基准测试.其中一个,我在一个循环中运行了一百万个sin/cos计算.在伪代码中,它看起来像这样:
var x: Double = 0.0
for (i <- 0 to 100 000 000)
x = sin(x)^2 + cos(x)^2
return x.toInteger
Run Code Online (Sandbox Code Playgroud)
如果我使用以下形式使用LLVM IR内联汇编来计算sin/cos:
%sc = call { double, double } asm "fsincos", "={st(1)},={st},1,~{dirflag},~{fpsr},~{flags}" (double %"res") nounwind
Run Code Online (Sandbox Code Playgroud)
这比分别使用fsin和fcos而不是fsincos更快.但是,它比我分别调用llvm.sin.f64and llvm.cos.f64intrinsics,编译调用C math lib函数要慢,至少使用我正在使用的目标设置(x86_64启用了SSE).
似乎LLVM在单/双精度FP之间插入一些转换 - 这可能是罪魁祸首.这是为什么?对不起,我是大会上的新手:
.globl main
.align 16, 0x90
.type main,@function
main: # @main
.cfi_startproc
# BB#0: # %loopEntry1
xorps %xmm0, %xmm0
movl $-1, %eax
jmp .LBB44_1
.align 16, 0x90
.LBB44_2: # %then4
# in Loop: Header=BB44_1 Depth=1 …Run Code Online (Sandbox Code Playgroud) 我有一个简短的程序,它执行数值计算,并在某些特定条件成立时获得不正确的 NaN 结果。我看不出这个 NaN 结果是如何产生的。请注意,我没有使用允许重新排序算术运算的编译器选项,例如-ffath-math.
问题:我正在寻找 NaN 结果如何产生的解释。从数学上讲,计算中没有任何内容会导致除以零或类似的结果。我错过了一些明显的东西吗?
\n请注意,我不是问如何解决问题\xe2\x80\x94,这很简单。我只是想了解 NaN 是如何出现的。
\n请注意,这个示例非常脆弱,甚至需要进行很小的修改,例如添加printf()在循环中添加调用以观察值)也会改变行为。这就是为什么我无法进一步减少它。
// prog.c\n\n#include <stdio.h>\n#include <math.h>\n\ntypedef long long myint;\n\nvoid fun(const myint n, double *result) {\n double z = -1.0;\n double phi = 0.0;\n for (myint i = 0; i < n; i++) {\n double r = sqrt(1 - z*z);\n\n /* avoids division by zero when r == 0 */\n if (i != 0 && i != n-1) {\n phi …Run Code Online (Sandbox Code Playgroud) 当我尝试编译此代码时:
#include <stdio.h>
main(int argc, char *argv[]) {
double y = 0;
__asm__ ("fldl $150;"
"fsqrt;"
"fstl %0;" : : "g" (y) );
printf("%f\n", y);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我收到此错误:
sqrt.c: Assembler messages:
sqrt.c:6: Error: suffix or operands invalid for `fld'
Run Code Online (Sandbox Code Playgroud)
为什么这不起作用?为什么我不能将数字"150"推入堆栈进行浮点运算?