assembly 展开的for循环之间的程序集差异会导致不同的浮点结果

vkc1a9a2  于 2022-11-24  发布在  其他
关注(0)|答案(2)|浏览(141)

请考虑以下设置:

typedef struct
{
    float d;
} InnerStruct;

typedef struct
{
    InnerStruct **c;
} OuterStruct;

float TestFunc(OuterStruct *b)
{
    float a = 0.0f;
    for (int i = 0; i < 8; i++)
        a += b->c[i]->d;
    return a;
}

TestFunc中的for循环完全复制了我正在测试的另一个函数中的一个循环,这两个循环都是由gcc(4.9.2)展开的,但在这样做之后产生的汇编略有不同。
我的测试循环的汇编:原始循环的汇编:

lwz       r9,-0x725C(r13)                   lwz       r9,0x4(r3)    
lwz       r8,0x4(r9)                        lwz       r8,0x8(r9)    
lwz       r10,0x0(r9)                       lwz       r10,0x4(r9)   
lwz       r11,0x8(r9)                       lwz       r11,0x0C(r9)  
lwz       r4,0x4(r8)                        lwz       r3,0x4(r8)    
lwz       r10,0x4(r10)                      lwz       r10,0x4(r10)  
lwz       r8,0x4(r11)                       lwz       r0,0x4(r11)   
lwz       r11,0x0C(r9)                      lwz       r11,0x10(r9)  
efsadd    r4,r4,r10                         efsadd    r3,r3,r10
lwz       r10,0x10(r9)                      lwz       r8,0x14(r9)   
lwz       r7,0x4(r11)                       lwz       r10,0x4(r11)  
lwz       r11,0x14(r9)                      lwz       r11,0x18(r9)  
efsadd    r4,r4,r8                          efsadd    r3,r3,r0
lwz       r8,0x4(r10)                       lwz       r0,0x4(r8)    
lwz       r10,0x4(r11)                      lwz       r8,0x0(r9)    
lwz       r11,0x18(r9)                      lwz       r11,0x4(r11)  
efsadd    r4,r4,r7                          efsadd    r3,r3,r10
lwz       r9,0x1C(r9)                       lwz       r10,0x1C(r9)  
lwz       r11,0x4(r11)                      lwz       r9,0x4(r8)    
lwz       r9,0x4(r9)                        efsadd    r3,r3,r0
efsadd    r4,r4,r8                          lwz       r0,0x4(r10)   
efsadd    r4,r4,r10                         efsadd    r3,r3,r11
efsadd    r4,r4,r11                         efsadd    r3,r3,r9
efsadd    r4,r4,r9                          efsadd    r3,r3,r0

问题是这些指令返回的浮点值并不完全相同。而且我不能改变原来的循环。我需要以某种方式修改测试循环以返回相同的值。我相信测试的组装等价于一个接一个地添加每个元素。我对组装不是很熟悉,所以我不确定上述差异如何翻译成c。我知道这是一个问题,因为如果我添加一个打印到循环,他们不展开和结果完全符合预期。

mkh04yzy

mkh04yzy1#

我想这是为了用一个函数对另一个函数进行单元测试。
一般来说,浮点计算在C或C中从来都不精确,期望它们精确是合理的。
Java语言标准要求精确的浮点结果。这样做是hatred against Java的一个恒定来源,有各种各样的指责,使结果可重复通常使它们不太准确,有时也使代码慢得多。
如果你用C或C
做测试,那么我建议你使用这种方法:
尽可能计算结果,既要高精度,又要高准确度,本例中输入的数据是32位浮点数,所以在计算预期结果之前,先将其全部转换为64位浮点数。
如果输入是double类型(并且你没有更大的long double类型),那么将这些值按顺序排序,并将它们从最小到最大相加。这将导致最小的精度损失。
一旦得到预期的结果,然后测试函数输出是否在一定范围内与之匹配。
有两种方法可以设置将测试视为通过所需的准确度:
一种方法是检查数字的真实的物理意义是什么,以及您实际需要的准确度是多少。
另一种方法是只要求结果精确到理想结果的几个最低有效位以内,即:误差小于理想结果乘以FLT_EPSILON的几倍。

fslejnso

fslejnso2#

禁用fast-math似乎可以解决这个问题。感谢@njuffa的建议。我希望能够围绕这个优化设计测试函数,但似乎不太可能。至少我现在知道问题出在哪里了。感谢大家对这个问题的帮助!

相关问题