在C中,当支持±0.0
时,分配给-0.0
的{{1}}或+0.0
通常不会产生算术差异。虽然它们具有不同的位模式,但它们在算术上比较相同。
double
受到@Pascal Cuoq comment的启发,我正在寻找标准C中的一些功能,这些功能提供了算术上不同的结果。
注意:许多功能(例如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
)从sin()
返回+0.0
,从f(+0.0)
返回-0.0
。但这些并不能提供不同的算术结果。此外,2个结果不应同时为f(-0.0)
。
答案 0 :(得分:16)
有一些标准操作和功能在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
}
Output:
1/x f(+0):+inf != f(-0):-inf
atan2(x,-1) f(+0):+3.141593e+00 != f(-0):-3.141593e+00
printf("%+e") f(+0):+4.300000e+01 != f(-0):+4.500000e+01
copysign(x,1) f(+0):+1.000000e+00 != f(-0):-1.000000e+00
signbit() f(+0):+0.000000e+00 != f(-0):-2.147484e+09
pow(x,-odd) f(+0):+inf != f(-0):-inf
tgamma(x) f(+0):+inf != f(-0):+inf
注意:
我的gcc 4.8.2计算机上显示tgamma(x)
==
,其他人correctly !=
。
rsqrt()
,AKA 1/sqrt()
可能是未来的C标准函数。可能/可能也不行。
double zero = +0.0; memcpy(&zero, &x, sizeof x)
可以显示x
与+0.0
的位模式不同,但x
仍然可以是+0.0
。我认为某些FP格式有许多位模式+0.0
和-0.0
。 TBD。
答案 1 :(得分:5)
IEEE 754-2008函数rsqrt
(将在未来的ISO C标准中)在±0上返回±∞,这是非常令人惊讶的。并且tgamma
也会在±0上返回±∞。对于MPFR,mpfr_digamma
在±0上返回±∞的反向。
答案 2 :(得分:1)
我考虑过这种方法,但我不能在周末之前检查,所以有人可能会对此进行一些实验,如果他/她喜欢,或者只是告诉我这是无稽之谈:
生成-0.0f。应该可以通过指定一个下浮动表示的微小负常数来静态生成。
将此常量分配给volatile double并返回float。
通过改变位表示2次,我假设了 编译器特定的标准位表示为-0.0f现在在 变量。编译器不能在那里智能化,因为完全 其他值可能在这两个副本之间的易变量中。
将输入与0.0f进行比较。检测我们是否有0.0f / -0.0f案例
如果相等,则分配输入volitale double变量,然后返回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;
}
编辑:第二个想法:如果我们可以强制比较(0.0 || -0.0)低于最小可表示的非正规(次正规)浮点数或其负对应的值,并且没有第二个模式为-0.0在FP格式中f(精确),我们可以将转换放到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;
}
这可能不适用于花式舍入方法,可以防止从负值舍入到-0.0。