在没有SSE2的情况下,针对没有SSE2的英特尔处理器的Java运行时strictfp
是如何处理浮点非正规的?
即使将387 FPU设置为53位精度,它仍保持超大的指数范围:
策略包括重新计算导致模拟浮点的非正规值的操作,或沿着这种技术的线路的永久指数偏移,为OCaml配备63位浮点数,从指数借用一点以避免双重-四舍五入.
在任何情况下,除非可以静态地确定操作不下溢/溢出,否则我认为没有办法避免每个浮点计算至少有一个条件分支.如何处理异常(溢出/下溢)情况是我的问题的一部分,但这不能与表示的问题分开(永久指数偏移策略似乎意味着只需要检查溢出).
在C中你可以测试是否使用NaN加倍isnan(x)
.然而,许多在线地方,包括例如这个SO答案,说你可以简单地使用x!=x
.
是x!=x
在任何C说明书中这是保证测试,如果x为NaN的方法?我自己找不到它,我希望我的代码能够与不同的编译器一起工作.
当使用GCC(4.8.2)和Clang(3.5.1)编译时,以下程序(从此处改编)给出不一致的结果.特别是,GCC结果即使在何时FLT_EVAL_METHOD
也不会改变.
#include <stdio.h>
#include <float.h>
int r1;
double ten = 10.0;
int main(int c, char **v) {
printf("FLT_EVAL_METHOD = %d\n", FLT_EVAL_METHOD);
r1 = 0.1 == (1.0 / ten);
printf("0.1 = %a, 1.0/ten = %a\n", 0.1, 1.0 / ten);
printf("r1=%d\n", r1);
}
Run Code Online (Sandbox Code Playgroud)
测试:
$ gcc -std=c99 t.c && ./a.out
FLT_EVAL_METHOD = 0
0.1 = 0x1.999999999999ap-4, 1.0/ten = 0x1.999999999999ap-4
r1=1
$ gcc -std=c99 -mpfmath=387 t.c && ./a.out
FLT_EVAL_METHOD = 2
0.1 = 0x0.0000000000001p-1022, 1.0/ten = 0x0p+0
r1=1
$ …
Run Code Online (Sandbox Code Playgroud) 以下代码将在x86 32位计算机上为变量"e"和"f"输出不同的结果,但在x86 64位计算机上会产生相同的结果.为什么?从理论上讲,正在评估相同的表达,但从技术上讲,它不是.
#include <cstdio>
main()
{
double a,b,c,d,e,f;
a=-8988465674311578540726.0;
b=+8988465674311578540726.0;
c=1925283223.0;
d=4294967296.0;
e=(c/d)*(b-a)+a;
printf("%.80f\n",e);
f=c/d;
f*=(b-a);
f+=a;
printf("%.80f\n",f);
}
Run Code Online (Sandbox Code Playgroud)
注意...使用'gcc -m32'可以生成32位x86代码,谢谢@Peter Cordes /sf/users/15689271/
也可以看看
boost :: random :: uniform_real_distribution应该是跨处理器的相同吗?
---为用户Madivad更新
64 bit output
-930037765265417043968.00000...
-930037765265417043968.00000...
32 bit output
-930037765265416519680.00000...
-930037765265417043968.00000...
Run Code Online (Sandbox Code Playgroud)
这个python代码可以给出"数学上正确"的输出
from fractions import Fraction
a=-8988465674311578540726
b=8988465674311578540726
c=1925283223
d=4294967296
print "%.80f" % float(Fraction(c,d)*(b-a)+a)
-930037765265416519680.000...
Run Code Online (Sandbox Code Playgroud) gcc/clang中是否有一个标志指定中间浮点计算的精度?
假设我有一个C代码
double x = 3.1415926;
double y = 1.414;
double z = x * y;
Run Code Online (Sandbox Code Playgroud)
是否有编译器标志允许以用户机器的最高精度计算'x*y',例如,long-double(64位尾数),然后截断为double(53位尾数,声明变量类型的精度)?
仅供参考,我在64位计算机上使用Ubuntu 14.04.
我最近分析了一个用VS2005编译的旧代码,因为"debug"(无优化)和"release"(/ O2/Oi/Ot选项)编译中的数字行为不同.(简化)代码如下所示:
void f(double x1, double y1, double x2, double y2)
{
double a1, a2, d;
a1 = atan2(y1,x1);
a2 = atan2(y2,x2);
d = a1 - a2;
if (d == 0.0) { // NOTE: I know that == on reals is "evil"!
printf("EQUAL!\n");
}
Run Code Online (Sandbox Code Playgroud)
f
如果使用相同的值对(例如f(1,2,1,2)
)调用,则该函数应打印"EQUAL" ,但这并不总是发生在"release"中.实际上,编译器已经优化了代码,好像它是完全相同的d = a1-atan2(y2,x2)
并且完全删除了对中间变量的赋值a2
.此外,它利用了第二个atan2()
结果已经在FPU堆栈上的事实,因此a1
在FPU上重新加载并减去这些值.问题是FPU以扩展精度(80位)工作,而a1
"仅"加倍(64位),因此将第一个atan2()
结果保存在内存中实际上已经失去了精度.最终,d
包含扩展精度和双精度之间的"转换错误".
我完全知道==
应该避免使用float/double的身份(操作符).我的问题不是关于如何检查双打之间的接近程度.我的问题是如何考虑"契约"对局部变量的赋值.按照我的"天真"观点,赋值应强制编译器将值转换为变量类型表示的精度(在我的例子中为double).如果变量是"浮动"怎么办?如果他们是"int"(很奇怪,但合法)怎么办?
那么,简而言之,C标准对这些案例有何评价?