C 在+;0.0和-0.0给出不同的算术结果?
在C语言中,当支持C 在+;0.0和-0.0给出不同的算术结果?,c,floating-point,C,Floating Point,在C语言中,当支持±0.0时,分配给双精度的-0.0或+0.0通常不会产生算术差异。虽然它们有不同的位模式,但它们在算术上是相等的 double zp = +0.0; double zn = -0.0; printf("0 == memcmp %d\n", 0 == memcmp(&zn, &zp, sizeof zp));// --> 0 == memcmp 0 printf("== %d\n", zn == zp);
±0.0
时,分配给双精度的-0.0
或+0.0
通常不会产生算术差异。虽然它们有不同的位模式,但它们在算术上是相等的
double zp = +0.0;
double zn = -0.0;
printf("0 == memcmp %d\n", 0 == memcmp(&zn, &zp, sizeof zp));// --> 0 == memcmp 0
printf("== %d\n", zn == zp); // --> == 1
受a的启发,我正在寻找标准C中提供算术上不同结果的更多函数
#include <math.h>
double inverse(double x) { return 1/x; }
double atan2m1(double y) { return atan2(y, -1.0); }
double sprintf_d(double x) {
char buf[20];
// sprintf(buf, "%+f", x); Changed to e
sprintf(buf, "%+e", x);
return buf[0]; // returns `+` or `-`
}
double copysign_1(double x) { return copysign(1.0, x); }
double signbit_d(double x) {
int sign = signbit(x); // my compile returns 0 or INT_MIN
return sign;
}
double pow_m1(double x) { return pow(x, -1.0); }
void zero_test(const char *name, double (*f)(double)) {
double fzp = (f)(+0.0);
double fzn = (f)(-0.0);
int differ = fzp != fzn;
if (fzp != fzp && fzn != fzn) differ = 0; // if both NAN
printf("%-15s f(+0):%-+15e %s f(-0):%-+15e\n",
name, fzp, differ ? "!=" : "==", fzn);
}
void zero_tests(void) {
zero_test("1/x", inverse);
zero_test("atan2(x,-1)", atan2m1);
zero_test("printf(\"%+e\")", sprintf_d);
zero_test("copysign(x,1)", copysign_1);
zero_test("signbit()", signbit_d);
zero_test("pow(x,-odd)", pow_m1);; // @Pascal Cuoq
zero_test("tgamma(x)", tgamma); // @vinc17 @Pascal Cuoq
}
注意:许多函数,如sin()
,从f(+0.0)
返回+0.0
,从f(-0.0)
返回-0.0
。但这些并没有提供不同的算术结果。另外,这两个结果不应同时为NaN
在f(+0.0)
和f(-0.0)
之间,有一些标准操作和函数形成了数值上不同的答案
不同的舍入模式或其他浮点实现可能会给出不同的结果
#include <math.h>
double inverse(double x) { return 1/x; }
double atan2m1(double y) { return atan2(y, -1.0); }
double sprintf_d(double x) {
char buf[20];
// sprintf(buf, "%+f", x); Changed to e
sprintf(buf, "%+e", x);
return buf[0]; // returns `+` or `-`
}
double copysign_1(double x) { return copysign(1.0, x); }
double signbit_d(double x) {
int sign = signbit(x); // my compile returns 0 or INT_MIN
return sign;
}
double pow_m1(double x) { return pow(x, -1.0); }
void zero_test(const char *name, double (*f)(double)) {
double fzp = (f)(+0.0);
double fzn = (f)(-0.0);
int differ = fzp != fzn;
if (fzp != fzp && fzn != fzn) differ = 0; // if both NAN
printf("%-15s f(+0):%-+15e %s f(-0):%-+15e\n",
name, fzp, differ ? "!=" : "==", fzn);
}
void zero_tests(void) {
zero_test("1/x", inverse);
zero_test("atan2(x,-1)", atan2m1);
zero_test("printf(\"%+e\")", sprintf_d);
zero_test("copysign(x,1)", copysign_1);
zero_test("signbit()", signbit_d);
zero_test("pow(x,-odd)", pow_m1);; // @Pascal Cuoq
zero_test("tgamma(x)", tgamma); // @vinc17 @Pascal Cuoq
}
注:
tgamma(x)
出现在我的GCC4.8.2机器上,但是=代码>在其他人身上
rsqrt()
,AKA1/sqrt()
可能是未来的C标准函数。可能/可能不起作用
double zero=+0.0;memcpy(&zero,&x,sizeof x)
可以显示x
是与+0.0
不同的位模式,但是x
仍然可以是+0.0
。我认为一些FP格式有许多位模式,它们是+0.0
和-0.0
。待定
这是IEEE 754-2008函数rsqrt(将在未来的ISO C标准中)返回的自我回答∞ 在±0上,这是非常令人惊讶的。和tgamma
也返回±∞ 在±0。使用MPFR时,MPFR_digamma
返回的是±∞ 在±0上。我考虑过这个方法,但我不能在周末前检查,所以如果有人愿意,他/她可能会做一些实验,或者告诉我这是胡说八道:
- 生成-0.0f。通过分配一个微小的负常数,可以静态地生成浮动表示
- 将该常数指定给volatile double并返回到float
通过更改位表示2次,我假设
-0.0f的编译器特定标准位表示现在位于
变量编译器在这方面比不上我,因为
其他值可能位于这两个副本之间的volatile变量中
- 将输入值与0.0f进行比较。要检测是否有0.0f//0.0f案例
- 如果相等,则指定输入的volitale双变量,然后返回float
我再次假设它现在有0.0f的标准编译器表示
- 通过并集访问位模式并进行比较,以确定其是否为-0.0f
代码可能类似于:
typedef union
{
float fvalue;
/* assuming int has at least the same number of bits as float */
unsigned int bitpat;
} tBitAccess;
float my_signf(float x)
{
/* assuming double has smaller min and
other bit representation than float */
volatile double refitbits;
tBitAccess tmp;
unsigned int pat0, patX;
if (x < 0.0f) return -1.0f;
if (x > 0.0f) return 1.0f;
refitbits = (double) (float) -DBL_MIN;
tmp.fvalue = (float) refitbits;
pat0 = tmp.bitpat;
refitbits = (double) x;
tmp.fvalue = (float) refitbits;
patX = tmp.bitpat;
return (patX == pat0)? -1.0f : 1.0f;
}
typedef联合
{
浮动价值;
/*假设int的位数至少与float的位数相同*/
无符号整数比特;
}tBitAccess;
浮动我的签名F(浮动x)
{
/*假设double具有较小的min和
浮点以外的其他位表示*/
易失性双比特;
tBitAccess tmp;
无符号整数pat0,patX;
如果(x<0.0f)返回-1.0f;
如果(x>0.0f)返回1.0f;
二进制位=(双)(浮点)-DBL_最小值;
tmp.fvalue=(浮点)位;
pat0=tmp.bitpat;
位=(双)x;
tmp.fvalue=(浮点)位;
patX=tmp.bitpat;
返回(patX==pat0)?-1.0f:1.0f;
}
- 它不是标准函数或运算符,而是一个应区分-0.0和0.0符号的函数
- 它(主要)基于这样一种假设,即编译器供应商不会因为格式的改变而对-0.0f使用不同的位模式,即使浮点格式允许,并且如果这样,它独立于所选的位模式
- 对于-0.0f具有精确模式的浮点格式,此函数应安全地执行此操作,而不需要知道该模式中的位顺序
- 其他假设(关于类型的大小等)可以通过float.h常量上的预编译器开关来处理
编辑:再想一想:如果我们可以强制将值与(0.0 | | |-0.0)进行比较,使之低于最小的可表示的非规范(次正常)浮点数或其负对应值,并且FP格式中的-0.0f(精确)没有第二种模式,我们可以将转换为volatile double。(但可能会使浮点保持不稳定,以确保在停用非规范化的情况下,编译器无法执行任何花哨的操作,忽略那些进一步将绝对值降低到0.0的操作。)
然后,代码可能如下所示:
typedef union
{
float fvalue;
/* assuming int has at least the same number of bits as float */
unsigned int bitpat;
} tBitAccess;
float my_signf(float x)
{
volatile tBitAccess tmp;
unsigned int pat0, patX;
if (x < 0.0f) return -1.0f;
if (x > 0.0f) return 1.0f;
tmp.fvalue = -DBL_MIN;
/* forcing something compares equal to 0.0f below smallest subnormal
- not sure if one abs()-factor is enough */
tmp.fvalue = tmp.fvalue * fabsf(tmp.fvalue);
pat0 = tmp.bitpat;
tmp.fvalue = x;
tmp.fvalue = tmp.fvalue * fabsf(tmp.fvalue);
patX = tmp.bitpat;
return (patX == pat0)? -1.0f : 1.0f;
}
typedef联合
{
浮动价值;
/*假设int的位数至少与float的位数相同*/
无符号整数比特;
}tBitAccess;
浮动我的签名F(浮动x)
{
挥发性tBitAccess tmp;
无符号整数pat0,patX;
如果(x<0.0f)返回-1.0f;
如果(x>0.0f)返回1.0f;
tmp.fvalue=-DBL_MIN;
/*强制某事物比最小次正常值低0.0f
-不确定一个abs()系数是否足够*/
tmp.fvalue=tmp.fvalue*fabsf(tmp.fvalue);
pat0=tmp.bitpat;
tmp.fvalue=x;
tmp.fvalue=tmp.fvalue*fabsf(tmp.fvalue);
patX=tmp.bitpat;
返回(patX==pat0)?-1.0f:1.0f;
}
这可能不适用于花哨的舍入方法,这种方法可以防止从负值舍入到-0.0。当您建议atan2
时,我印象深刻,当您添加sprintf
时,我印象更深刻。从那时起,我就想不出任何进一步的函数来区分+0和-0了。@Pascal Cuoq你的评论激发了这一努力。一些双曲函数,如csch()
,也可以区分,但它们不会出现在C标准中。除了1/0
导致坏事情之外,1/x
似乎