有没有办法用i387 fsqrt指令进行正确的舍入?...
... 除了改变 x87控制字中的精度模式 - 我知道这是可能的,但它不是一个合理的解决方案,因为它有令人讨厌的重入类型问题,如果sqrt操作被中断,精度模式将是错误的.
我正在处理的问题如下:x87 fsqrt操作码在fpu寄存器的精度中执行正确舍入(按IEEE 754)平方根操作,我假设它是扩展(80位)精度.但是,我想用它来实现高效的单精度和双精度平方根函数,并且结果正确舍入(按照当前的舍入模式).由于结果具有过高的精度,因此将结果转换为单精度或双精度的第二步再次舍入,可能会留下不正确舍入的结果.
通过一些操作,可以通过偏差来解决这个问题.例如,我可以通过以2的幂的形式添加偏置来避免过度结果的过度精度,该偏置将双精度值的52个有效位强制为63位扩展精度尾数的最后52位.但我没有看到任何明显的方法用平方根做这样的技巧.
任何聪明的想法?
(还标记为C,因为预期的应用程序是C sqrt和sqrtf函数的实现.)
请考虑以下代码:
// Filename fputest.cpp
#include <cmath>
#include <cstdio>
int main()
{
double x;
*(__int64 *) &x = 0xc01448ec3aaa278di64; // -5.0712136427263319
double sine1 = sin(x);
printf("%016llX\n", sine1);
double sine2;
__asm {
fld x
fsin
fstp sine2
}
printf("%016llX\n", sine2);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用Visual C++ 2012(cl fputest.cpp)编译并执行程序时,输出如下:
3FEDF640D8D36174
3FEDF640D8D36175
Run Code Online (Sandbox Code Playgroud)
问题:
我正在阅读英特尔指令手册并注意到有一条'NOP'指令在主CPU上没有任何作用,而且一条'FNOP'指令在FPU上什么都不做.为什么有两个单独的指令什么都不做?
我看到的唯一不同之处是它们会抛出不同的异常,因此您可能会从FNOP中查看异常,以检测是否有可用的FPU.但是没有像CPUID这样的其他机制来检测这个吗?有什么实际的理由有两个单独的NOP指令?
可以检查x87浮点控制字的值_control87.当一个新线程启动时,在我的平台上,它似乎从父线程继承浮点控制字的值.
这是未定义的行为,还是我保证,如果我启动一个新线程,并且线程库没有错误,控制字具有与父线程中相同的值?
在哪个标准中定义了这种行为,以及如何?如果未在任何标准中定义,是在处理器手册或操作系统文档中定义的?
我正在使用C++语言,在64位Windows 7上开发,编译32位Windows目标,并使用x86兼容处理器执行代码.我需要专门针对此平台的答案,但如果所有语言和处理器的行为相同,那么通用答案会更好.
我试图将80位扩展精度浮点数(在缓冲区中)转换为double.缓冲区基本上包含x87寄存器的内容.
这个问题帮助我开始,因为我并不熟悉IEEE标准.无论如何,我很难找到80位格式的次正规(或非规范化)数字的有用信息.我所知道的是,与float32或float64不同,它在尾数中没有隐藏位(没有隐含的1.0加法),所以知道数字是否规范化的一种方法是检查尾数中的最高位是否设置.这让我有以下问题:
根据维基百科告诉我的情况,float32和float64表示一个次正规数,其(偏差)指数为0,尾数为非零尾数.
编辑:我想这个问题归结为:
我可以期望FPU清理x87寄存器中的指数和最高尾数位吗?
如果不是,转换结果应该是什么类型的数字?在这种情况下,我应该完全忽略指数吗?还是qNaN?
编辑:
我阅读了英特尔手册(英特尔®64和IA-32架构软件开发人员手册,第1卷:基础架构)中的FPU部分,这比我担心的要少.事实证明,未定义以下值:
它没有提到这些值是否可以在野外出现,也不会在内部转换.所以我实际上除了Ollydbg并手动设置x87寄存器中的位.我制作了ST(0)来包含指数中设置的所有位和尾数为0.然后我让它执行
FSTP QWORD [ESP]
FLD QWORD [ESP]
Run Code Online (Sandbox Code Playgroud)
存储的值[ESP]转换为信令NaN.之后FLD,ST(0)载着一个安静的NaN.
我猜这回答了我的问题.我接受了J-16 SDiZ的解决方案,因为它是最直接的解决方案(虽然它没有明确解释一些更精细的细节).
无论如何,案件解决了.谢谢大家.
为什么使用如此"怪异"的寄存器大小?关于为什么不优选使用64或128位这些寄存器的任何文档?
在我的大学,我们刚刚介绍了IA32 x87 FPU.但是我们没有被告知如何清除FPU-Stack不再需要的元素.
想象一下,我们正在执行一个简单的计算,如(5.6*2.4)+(3.9*10.3).
.data
value1: .float 5.6
value2: .float 2.4
value3: .float 3.8
value4: .float 10.3
output: .string "The result is: %f\n"
.text
.global main
main:
fld value1 # Load / Push 5.6 into FPU
fmul value2 # Multiply FPU's top (5.6) with 2.4
fld value3 # Load / Push 3.8 into FPU
fmul value4 # Multiply the top element of the FPU's Stacks with 10.3
fadd %st(1) # Add the value under the top element to the top …Run Code Online (Sandbox Code Playgroud) 我注意到编译器每次使用double算术时都会生成针对SIMD寄存器的代码.这适用于非优化和优化的代码.这是否意味着x87 FP单元可以被认为是过时的,仅用于向后兼容?
我还注意到其他"流行"平台也依赖于各自的SIMD实现而不是FP设计为堆栈.
此外,SIMD实现往往至少为128位宽,所以我想这是否意味着(内部)操作精度高于x87 FP单元?
我也想知道关于性能,吞吐量和延迟,考虑到SIMD已设想在考虑向量执行,所以我不知道他们如何与标量做.
在 Intel 处理器上,由于使用 pi 的 66 位近似值,x87 三角指令(例如 FSIN)的精度有限,即使计算本身精确到 80 位扩展精度浮点运算的完整 64 位尾数。点值。(所有有效输入的完全准确性需要 pi 的 128 位近似值。)英特尔文档中的遗漏在问题引起注意后得到了纠正。
然而,除了《AMD64 架构程序员手册》第 1 卷中提到的内容之外,我找不到有关 AMD x87 三角指令实现准确性的类似详细信息:
6.4.5.1 超越结果的准确性
x87 计算以双扩展精度格式执行,以便超越函数为每种浮点数据类型提供精确到最后一位 (ulp)一个单位的结果。
对于所有有效输入(包括 128 位或更好的 pi 近似值),AMD 的 x87 三角指令实现实际上是否完全精确到扩展精度格式的一个 ULP 之内?与Zen和Zen 2架构(Ryzen 和 EPYC)相关的答案将是理想的。
floating-point trigonometry floating-accuracy x87 amd-processor
如果我有 2 个具有不同位模式的非正规浮点数并比较它们是否相等,结果是否会受到非正规数-零标志、刷新到零标志或常用处理器上的其他标志的影响?
还是这些标志只影响计算而不影响相等性检查?