system
1
两个随机产生的float矩阵,在Win32平台上进行乘法操作,分别使用:
- cublas Sgemm
- eigen library
- 自己写的cpu 的MATMUL code
3种方法所得结果均有细微差别,体现在内存上的差据大约是每个float的 32位(4个byte )中,负责小数位的后23位中的最后2~3位不同。
后又用相同随机数种子生成double矩阵,使用eigen和自己的code在CPU上运算,所得结果(只看前内存上小数位的前23位)又与前三种方法得到的结果不同。
想知道具体什么原因,是因为运算顺序不同还是其他原因。能否告知确切原因,最好有办法可以试验证明。如果有遇到相同问题的朋友,希望可以一起交流讨论。
谢谢。
system
2
LZ您好:
您说的这个情况是正常的,以及这并不是某种错误。
因为我们使用的浮点数是一种有限精度的数,它只是对实数的一种近似,并不满足实数所具有的交换律和结合律。在不同顺序计算和不同结合方式计算的情况下,结果会有一定差异,在设计算法的时候,需要认识到这种差别并且预估到这种差别的影响程度。
举个例子而言,假定有1000个浮点数,需要相加,正序相加,倒序相加和规约相加,最终得到的结果是不保证相同的,而且如果所有的数据同号且大小都比较平均的话,使用规约相加的实际精度更高一些,因为另外两种计算到后面的时候,累加的和的绝对值已经比较大,会吃掉单个元素的精度(有较大的截断误差)。
而您所说的情况,三种实现方法的具体实现细节并不相同,计算顺序也不相同,GPU库往往会采用分块并行计算的方法,这个和CPU上的实现顺序差别较大,因此如您所述,只在最后的几个bit有差别,这是完全正常的。同时也不能说明三者里面,哪个更准确,这只是三种具体的浮点计算下的结果。
system
3
其二:您使用的设备也是不同的。
您使用了CPU和GPU两个设备,虽然两者都是IEEE 754标准兼容的,但是在一些细节上还有不同。
如果您在CPU上计算时,实际使用了X87的浮点计算,那么中间过程是计算到80bit精度的,最后截断,在中间过程精度上有优势。
如果您在CPU上计算时,实际使用了一些SIMD指令的浮点计算,那么将不享有X87那样的中间精度(但是速度快很多)。
以及,您在GPU上的计算,主流GPU都具有FMA能力,在计算乘加的时候可以保留更多的中间步骤精度,而除了最新的部分CPU以外,多数CPU并不具备这个能力。
这样也会带来计算结果的轻微差异。
system
4
第三,您使用double验证的问题:
您在使用double精度验证的时候,虽然使用了相同的随机化种子,但是此时您生成的随机数据并不能和之前使用相同种子生成的单精度数据划等号,因此您计算的数据来源就不同,结果当然会有所差异。
此外您的代码和某CPU库的计算顺序必然也是不同的,因此结果不相同也是正常的。
因为整个计算的源数据就不同,加上double计算累积,截断与之前不同,所以前23bit与之前的结果不同也是完全正常的(假定您的代码并无BUG影响)。
您的问题大致分析如此。
最后引用一句出处不可考的话:“没有误差,就没有科学。”计算误差是客观存在的,(当然测量误差等其他误差也是如此),作为使用者需要认识和评估这种存在,使得在有误差的情况下,依然保证算法的可靠性,或者预估算法在什么程度上可靠;而不能想要消除误差或者想要强制在不同的计算实现下有着完全一致的误差表现,这个做不到。
大致如此,供您参考。
祝您编码顺利~