Нет, это не гарантировано. Если вы используете NETLIB BLAS без каких-либо оптимизаций, в большинстве случаев верно, что результаты совпадают. Но для любого практического использования BLAS и LAPACK используется высокооптимизированный параллельный BLAS. Распараллеливание приводит к тому, что, даже если оно работает только параллельно внутри векторных регистров ЦП, меняется порядок вычисления отдельных членов и изменяется порядок суммирования. Теперь из отсутствующего ассоциативного свойства в стандарте IEEE следует, что результаты не совпадают. Так что именно то, что вы упомянули, может произойти.
В NETLIB BLAS скалярное произведение является только циклом for, развернутым в 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
и это зависит от компилятора, если каждое умножение добавляется в DTEMP немедленно или если все 5 компонентов суммируются первыми и затем добавляются в DTEMP. В OpenBLAS ядро зависит от архитектуры:
__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"
...
который разбивает скалярное произведение на маленькие скалярные произведения длины 4 и суммирует их.
При использовании других типичных реализаций BLAS, таких как ATLAS, MKL, ESSL, ... эта проблема остается той же, поскольку каждая реализация BLAS использует различные оптимизации для получения быстрого кода. Но, насколько я знаю, нужен искусственный пример, чтобы вызвать действительно ошибочные результаты.
Если необходимо, чтобы библиотека BLAS возвращала для тех же результатов (побитовых и одинаковых), необходимо использовать воспроизводимую библиотеку BLAS, такую как: