众所周知,IEEE浮点乘法不是关联的。但是,请考虑a,b和c是32位有符号整数(在C中)的特殊情况:
double da = (double) a;
double db = (double) b;
double dc = (double) c;
现在,da*(db*dc) == (da*db)*dc
总是返回1吗?这里,双精度是64位双精度IEEE foating点。正在使用Round-to-even。
我手动尝试了几个示例,例如a = pow(2, 30) + 1
,b = pow(2, 30)+1
,c = pow(2, 30) + pow(2, 6)
,数字足够大以确保会涉及到某些舍入,因为确切的数学答案没有任何表示
不幸的是我找不到反例。这个表达式看起来可能会返回0.它可能总是返回1吗?为什么呢?
答案 0 :(得分:3)
我设法找到了一个反例。考虑a = pow(2,30) + 1
,b = pow(2,30) + 1
,c = pow(2,30) + pow(2, 6) + 1
。
然后,如果使用round-to-even手动计算,我们得到:
(da*db)*dc == pow(2, 90) + pow(2, 66) + pow(2, 61) + pow(2, 60) + pow(2, 38)
另一方面:
da*(db*dc) == pow(2, 90) + pow(2, 66) + pow(2, 61) + pow(2, 60)
请注意,这两个结果的二进制表示仅在最后一位有所不同。实际上,90-38 = 52,是64位双精度浮点的二进制表示中的最低有效位。
答案 1 :(得分:2)
对于这样的问题,我的第一个冲动是尝试使用随机数进行蛮力搜索。在这种情况下,生成具有均匀分布的随机32位整数表明大约三分之一的情况为da*(db*dc) != (da*db)*dc
。
在评论中,Pascal Cuoq提出了一个令人信服的论据,即最大的操作数必须是> 2 26 在不平等程度上的发生。这意味着我们可以进一步限制搜索到操作数< 2 27 的数量级:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
// Fixes via: Greg Rose, KISS: A Bit Too Simple. http://eprint.iacr.org/2011/007
static uint32_t z=362436069,w=521288629,jsr=362436069,jcong=123456789;
#define znew (z=36969*(z&0xffff)+(z>>16))
#define wnew (w=18000*(w&0xffff)+(w>>16))
#define MWC ((znew<<16)+wnew)
#define SHR3 (jsr^=(jsr<<13),jsr^=(jsr>>17),jsr^=(jsr<<5)) /* 2^32-1 */
#define CONG (jcong=69069*jcong+13579) /* 2^32 */
#define KISS ((MWC^CONG)+SHR3)
int main (void)
{
long long count = 0LL, fail = 0LL;
double minmax = 1.0 / 0.0;
do {
int32_t a = KISS & (((uint32_t)1 << 27) - 1);
int32_t b = KISS & (((uint32_t)1 << 27) - 1);
int32_t c = KISS & (((uint32_t)1 << 27) - 1);
volatile double da = (double) a;
volatile double db = (double) b;
volatile double dc = (double) c;
volatile double dab = da * db;
volatile double dbc = db * dc;
volatile double p1 = dab * dc;
volatile double p2 = da * dbc;
count++;
if (p1 != p2) {
double max = fmax (fabs(c), fmax (fabs(da), fabs(db)));
fail++;
if (max < minmax) {
minmax = max;
printf ("a=%08x b=%08x c=%08x p1=% 22.13a p2=% 22.13a minmax=%08x count=%13lld fail=%13lld\n",
a, b, c, p1, p2, (int32_t)minmax, count, fail);
}
}
} while (1);
return EXIT_SUCCESS;
}
上述代码的输出强烈表明,如果产品da*db
或产品db*dc
的数量超过2 53 ,则会出现不平等,并且最大因此,因子必须大于√2 53 ,即必须大于0x05A82799
。
a=0658c99a b=066c9dd5 c=0679e799 p1= 0x1.080f1700c8935p+80 p2= 0x1.080f1700c8934p+80 minmax=0679e799 count= 48 fail= 1
a=05b06599 b=062ad373 c=0303ee05 p1= 0x1.a72fc3446c46ep+78 p2= 0x1.a72fc3446c46dp+78 minmax=062ad373 count= 248 fail= 6
a=05b3b91a b=05c6607f c=061761fd p1= 0x1.9129296aa189ap+79 p2= 0x1.9129296aa189bp+79 minmax=061761fd count= 552 fail= 14
a=05ffbe73 b=055f71db c=0413771e p1= 0x1.06c12351727fbp+79 p2= 0x1.06c12351727fcp+79 minmax=05ffbe73 count= 1029 fail= 33
a=05ae5163 b=05e25901 c=017bd0b4 p1= 0x1.8cc25e308318ap+77 p2= 0x1.8cc25e3083189p+77 minmax=05e25901 count= 1160 fail= 41
a=0349eff2 b=05a40dd7 c=05b2b3c7 p1= 0x1.a6d583d48b807p+78 p2= 0x1.a6d583d48b806p+78 minmax=05b2b3c7 count= 3187 fail= 129
a=05ae99c5 b=05a4e5a1 c=03d88fe6 p1= 0x1.ed5c24a47c416p+78 p2= 0x1.ed5c24a47c417p+78 minmax=05ae99c5 count= 43479 fail= 1722
a=05626e68 b=05ad8275 c=05acdbe3 p1= 0x1.5b017380396c4p+79 p2= 0x1.5b017380396c5p+79 minmax=05ad8275 count= 49985 fail= 1952
a=01f7f82a b=05a9f16b c=05ad0265 p1= 0x1.fa48865afc8abp+77 p2= 0x1.fa48865afc8acp+77 minmax=05ad0265 count= 362414 fail= 14190
a=016047d9 b=05a5d671 c=05ab6f59 p1= 0x1.608381a720becp+77 p2= 0x1.608381a720bebp+77 minmax=05ab6f59 count= 1701498 fail= 66487
a=059ecf08 b=05a83be9 c=05aa9cf9 p1= 0x1.685523897b927p+79 p2= 0x1.685523897b926p+79 minmax=05aa9cf9 count= 3412539 fail= 132973
a=05a9203d b=05a8b04b c=050c6609 p1= 0x1.436f803e2c24bp+79 p2= 0x1.436f803e2c24ap+79 minmax=05a9203d count= 4074531 fail= 158854
a=00e22912 b=05a8f039 c=05a846ad p1= 0x1.c49a83e32465fp+76 p2= 0x1.c49a83e32465ep+76 minmax=05a8f039 count= 17021780 fail= 665646
a=03bc399a b=05a8bdf9 c=05a7ca6d p1= 0x1.de2fac272a096p+78 p2= 0x1.de2fac272a095p+78 minmax=05a8bdf9 count= 17716380 fail= 692903
a=054d26c1 b=05a84b09 c=05a8a8c7 p1= 0x1.53704223556b3p+79 p2= 0x1.53704223556b4p+79 minmax=05a8a8c7 count= 45074477 fail= 1762937
a=03afcaa4 b=05a88a8d c=05a7f119 p1= 0x1.d7f3ccaacca2dp+78 p2= 0x1.d7f3ccaacca2cp+78 minmax=05a88a8d count= 118913730 fail= 4649640
a=024efebb b=05a7e34d c=05a881b5 p1= 0x1.2783cfcc9de75p+78 p2= 0x1.2783cfcc9de74p+78 minmax=05a881b5 count= 254043151 fail= 9930225
a=05a7d8e7 b=05a87f8b c=050f538e p1= 0x1.43d6f3ad2f956p+79 p2= 0x1.43d6f3ad2f957p+79 minmax=05a87f8b count= 401539580 fail= 15695528
a=007790b6 b=05a859c9 c=05a852bb p1= 0x1.de61a9e2da63ap+75 p2= 0x1.de61a9e2da63bp+75 minmax=05a859c9 count= 455973582 fail= 17824719
a=01505b17 b=05a8270d c=05a830d1 p1= 0x1.505d1a5677dfbp+77 p2= 0x1.505d1a5677dfap+77 minmax=05a830d1 count= 1109123125 fail= 43353113
a=0161a988 b=05a82bed c=05a82917 p1= 0x1.61aaf393e420bp+77 p2= 0x1.61aaf393e420cp+77 minmax=05a82bed count= 6209738039 fail= 242736587
a=05a82a7d b=05a8267b c=01c962b9 p1= 0x1.c96347ff18b85p+77 p2= 0x1.c96347ff18b84p+77 minmax=05a82a7d count= 43616243136 fail= 1704950604
a=05a8293b b=05a828a3 c=01a44ac0 p1= 0x1.a44b86270a7d5p+77 p2= 0x1.a44b86270a7d6p+77 minmax=05a8293b count= 51943039028 fail= 2030420245
a=05a82769 b=05a8292d c=048dbc75 p1= 0x1.236f64a4577fap+79 p2= 0x1.236f64a4577fbp+79 minmax=05a8292d count= 476560049469 fail= 18628158958
a=05a82871 b=05a828fd c=01c044b1 p1= 0x1.c04561ac59666p+77 p2= 0x1.c04561ac59667p+77 minmax=05a828fd count= 846496019034 fail= 33088575026
a=003f4e0f b=05a828d7 c=05a828f7 p1= 0x1.fa71612c258eap+74 p2= 0x1.fa71612c258e9p+74 minmax=05a828f7 count= 864908706983 fail= 33808303937
a=043c7359 b=05a827db c=05a828d9 p1= 0x1.0f1d1e47c37e2p+79 p2= 0x1.0f1d1e47c37e3p+79 minmax=05a828d9 count=1147388053277 fail= 44850164685
a=05a82895 b=05a82887 c=02e5fc6a p1= 0x1.72feb235ca1e7p+78 p2= 0x1.72feb235ca1e6p+78 minmax=05a82895 count=1383932059721 fail= 54096517230
a=005b76e8 b=05a8284f c=05a82793 p1= 0x1.6ddbcc2614301p+75 p2= 0x1.6ddbcc2614300p+75 minmax=05a8284f count=2212385094246 fail= 86479620757
a=034f873b b=05a827f1 c=05a82755 p1= 0x1.a7c3a2fcb346bp+78 p2= 0x1.a7c3a2fcb346ap+78 minmax=05a827f1 count=2360223870382 fail= 92258517174
a=02b2ee37 b=05a827e7 c=05a82789 p1= 0x1.597729fe1b0f2p+78 p2= 0x1.597729fe1b0f3p+78 minmax=05a827e7 count=4588948516457 fail= 179376767823
a=0023898a b=05a827db c=05a82779 p1= 0x1.1c4c566dfffcbp+74 p2= 0x1.1c4c566dfffccp+74 minmax=05a827db count=6159639135691 fail= 240773437792
a=05a827a1 b=05a827bd c=0066ef2d p1= 0x1.9bbcc027b0c20p+75 p2= 0x1.9bbcc027b0c21p+75 minmax=05a827bd count=7528083873615 fail= 294264463804
答案 2 :(得分:1)
da*(db*dc) == (da*db)*dc
总是返回1吗?
受到@user394438的启发,他使用了2对2的幂来作为反例,我尝试了下面的方法来找到一个小的三a,b,c
反例。这符合@Pascal Cuoq评论的要求:a,b,c
中至少有1个需要超过2 26 。我相信需要更严格的条件:a,b,c
中至少有一个需要超过2 27 才能行使典型的53位二进制数字double
。在这种情况下0x8000001
= 2 27 + 1。
3,0x4000001,0x8000001
关键编码问题是使某些中间产品不使用long double
。即使(a*b)*c
,C也允许long double
执行double a,b,c
。查看FLT_EVAL_METHOD
或使用volatile
。另外,long double
使用其典型的64位精度,找到一个反例可能没有实际意义。
void test(volatile double a, volatile double b, volatile double c) {
volatile double bc = b * c;
volatile double ab = a * b;
volatile double p1 = ab * c;
volatile double p2 = a * bc;
// da*(db*dc) == (da*db)*dc ?
if (p1 != p2) {
static double sum_small = 1.0 / 0.0;
double sum = a + b + c;
if (sum < sum_small) {
sum_small = sum;
printf("%a\n%a\n", p1, p2);
printf("%x %x %x\n", (unsigned) a, (unsigned) b, (unsigned) c);
fflush(stdout);
}
}
}
double r() {
int p2a = rand()%31;
int p2b = rand()%31;
return pow(2,p2a) + pow(2,p2b);
}
int main() {
test(pow(2,30)+1, pow(2,30)+1, pow(2,30) + pow(2, 6) + 1);
for (unsigned long long i = 0; i < 1000ULL * 1000 * 1000 * 1000; i++) {
test(r(), r(), r());
//test(rand(), rand(), rand());
}
return 0;
}