No, eso no está garantizado. Si está utilizando un NETLIB BLAS sin optimizaciones, es cierto que los resultados son los mismos. Pero para cualquier uso práctico de BLAS y LAPACK se usa un BLAS paralelo altamente optimizado. La paralelización provoca, incluso si solo funciona en paralelo dentro de los registros vectoriales de una CPU, que el orden en que se evalúan los términos individuales cambia y el orden de la suma también cambia. Ahora se deduce de la propiedad asociativa que falta en el estándar IEEE que los resultados no son los mismos. Entonces, exactamente lo que mencionaste puede suceder.
En NETLIB BLAS, el producto escalar es solo un bucle for desenrollado por un factor 5:
DO I = MP1,N,5
DTEMP = DTEMP + DX(I)*DY(I) + DX(I+1)*DY(I+1) +
$ DX(I+2)*DY(I+2) + DX(I+3)*DY(I+3) + DX(I+4)*DY(I+4)
END DO
y depende del compilador si cada multiplicación se agrega a DTEMP inmediatamente o si los 5 componentes se suman primero y luego se agregan a DTEMP. En OpenBLAS depende de la arquitectura un núcleo más complicado:
__asm__ __volatile__
(
"vxorpd %%ymm4, %%ymm4, %%ymm4 \n\t"
"vxorpd %%ymm5, %%ymm5, %%ymm5 \n\t"
"vxorpd %%ymm6, %%ymm6, %%ymm6 \n\t"
"vxorpd %%ymm7, %%ymm7, %%ymm7 \n\t"
".align 16 \n\t"
"1: \n\t"
"vmovups (%2,%0,8), %%ymm12 \n\t" // 2 * x
"vmovups 32(%2,%0,8), %%ymm13 \n\t" // 2 * x
"vmovups 64(%2,%0,8), %%ymm14 \n\t" // 2 * x
"vmovups 96(%2,%0,8), %%ymm15 \n\t" // 2 * x
"vmulpd (%3,%0,8), %%ymm12, %%ymm12 \n\t" // 2 * y
"vmulpd 32(%3,%0,8), %%ymm13, %%ymm13 \n\t" // 2 * y
"vmulpd 64(%3,%0,8), %%ymm14, %%ymm14 \n\t" // 2 * y
"vmulpd 96(%3,%0,8), %%ymm15, %%ymm15 \n\t" // 2 * y
"vaddpd %%ymm4 , %%ymm12, %%ymm4 \n\t" // 2 * y
"vaddpd %%ymm5 , %%ymm13, %%ymm5 \n\t" // 2 * y
"vaddpd %%ymm6 , %%ymm14, %%ymm6 \n\t" // 2 * y
"vaddpd %%ymm7 , %%ymm15, %%ymm7 \n\t" // 2 * y
"addq $16 , %0 \n\t"
"subq $16 , %1 \n\t"
"jnz 1b \n\t"
...
que divide el producto escalar en pequeños productos escalares de longitud 4 y los resume.
Usando las otras implementaciones típicas de BLAS como ATLAS, MKL, ESSL, ... este problema permanece igual porque cada implementación de BLAS usa diferentes optimizaciones para obtener un código rápido. Pero hasta donde yo sé, uno necesita un ejemplo artificial para causar resultados realmente defectuosos.
Si es necesario que la biblioteca BLAS regrese para obtener los mismos resultados (en lo que respecta a los bits), se debe usar una biblioteca BLAS reproducible como: