Даны массивы из n дробных чисел (float): A, B, C, D, E. Вычислить математическое выражение, используя команды SSE
Я столкнулась с проблемой в коде на ассемблере, который выполняет вычисления с массивами чисел с плавающей точкой, используя команды SSE. Код должен вычислять значения для массива E, используя элементы массивов A, B, C и D по следующей формуле: e=(a+b)/d + c*d
При выполнении программы результаты в массиве E оказываются некорректными. Например, вместо ожидаемых значений, я получаю:
E[0] = 512.000123
E[1] = 262144.063538
E[2] = 33554440.187500
E[3] = 805306499.562500
E[4] = 17179873412.000000
E[5] = 171798725768.000000
Ожидаемый вывод:
E[0] = 5.0
E[1] = 10.0
E[2] = 17.0
E[3] = 26.0
E[4] = 37.0
E[5] = 50.0
Код:
section .data
n equ 6 ; Количество элементов
A dd 1.0, 2.0, 3.0, 4.0, 5.0, 6.0
B dd 1.0, 1.0, 1.0, 1.0, 1.0, 1.0
C dd 2.0, 3.0, 4.0, 5.0, 6.0, 7.0
D dd 2.0, 3.0, 4.0, 5.0, 6.0, 7.0
E dd 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
fmt db "E[%d] = %f", 10, 0 ; Формат для вывода
section .text
global main
extern printf
main:
push ebp
mov ebp, esp
xor ebx, ebx ; Инициализация индекса
.loop:
cmp ebx, n
jge .end_loop
; Загружаем значения
movss xmm0, [A + ebx * 4] ; A[i]
movss xmm1, [B + ebx * 4] ; B[i]
movss xmm2, [C + ebx * 4] ; C[i]
movss xmm3, [D + ebx * 4] ; D[i]
; Вычисление E[i] = C[i] * D[i] + (A[i] + B[i]) / D[i]
addss xmm0, xmm1 ; xmm0 = A[i] + B[i]
; Проверка на деление на ноль для D[i]
movss xmm4, xmm3 ; Копируем D[i] в xmm4
ucomiss xmm4, xmm4 ; Сравнение D[i] с самим собой
je .skip_div ; Если D[i] == 0, пропускаем деление
divss xmm0, xmm3 ; xmm0 = (A[i] + B[i]) / D[i]
jmp .continue_calculation ; Переход к продолжению вычисления
.skip_div:
xorps xmm0, xmm0 ; Установить xmm0 в 0.0, если деление на ноль
.continue_calculation:
mulss xmm2, xmm3 ; xmm2 = C[i] * D[i]
addss xmm2, xmm0 ; xmm2 = C[i] * D[i] + (A[i] + B[i]) / D[i]
; Хранение результата в E
movss [E + ebx * 4], xmm2 ; E[i] = Result
; Подготовка для printf
push ebx ; Помещаем текущий индекс
movss xmm0, [E + ebx * 4] ; Загружаем значение из E для printf
movss [esp], xmm0 ; Сохраняем его в памяти на верхушке стека
push dword [esp] ; Вторая запись в стек для float
push ebx ; Помещаем текущий индекс снова
push fmt ; Помещаем строку формата
call printf ; Вызываем printf
add esp, 12 ; Очищаем стек (4 для float + 4 для индекса + 4 для строки формата)
inc ebx ; Увеличиваем индекс
jmp .loop ; Повторяем цикл
.end_loop:
xor eax, eax ; Возвращаем 0
mov esp, ebp ; Восстанавливаем указатель на стек
pop ebp ; Восстанавливаем указатель на базу
ret
Вопрос: Как исправить код, чтобы он правильно вычислял значения в массиве E?
Ответы (1 шт):
Автор решения: Вероника
→ Ссылка
Проблема решена использованием cvtss2sd
(преобразованием в двойную точность):
section .data
n equ 6 ; Количество элементов (увеличено до 6)
A dd 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ; Массив A
B dd 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 ; Массив B
C dd 2.0, 2.0, 2.0, 2.0, 2.0, 2.0 ; Массив C
D dd 10.0, 10.0, 10.0, 10.0, 10.0, 10.0 ; Массив D
E dd 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ; Массив E для хранения результата
fmt db "E[%d] = %f", 10, 0 ; Формат для вывода
div_by_zero db "Division by zero!", 10, 0 ; Сообщение об ошибке
section .text
global main
extern printf
main:
push ebp
mov ebp, esp
xor ebx, ebx ; Инициализация индекса
.loop:
cmp ebx, n ; Сравниваем индекс с n
jge .end_loop ; Если индекс >= n, выходим из цикла
; Загружаем значения из массивов (single-precision)
movss xmm0, [A + ebx * 4] ; xmm0 = A[ebx]
movss xmm1, [B + ebx * 4] ; xmm1 = B[ebx]
movss xmm2, [C + ebx * 4] ; xmm2 = C[ebx]
movss xmm3, [D + ebx * 4] ; xmm3 = D[ebx]
; Проверка на деление на ноль
ucomiss xmm3, xmm0 ; Сравниваем D[ebx] с 0
je .div_zero_error ; Если D[ebx] = 0, переходим к обработке ошибки
; (A[ebx] + B[ebx]) / D[ebx]
addss xmm0, xmm1 ; xmm0 = A[ebx] + B[ebx]
divss xmm0, xmm3 ; xmm0 = (A[ebx] + B[ebx]) / D[ebx]
; C[ebx] * D[ebx]
mulss xmm2, xmm3 ; xmm2 = C[ebx] * D[ebx]
; Результат
addss xmm0, xmm2 ; xmm0 = (A[ebx] + B[ebx]) / D[ebx] + C[ebx] * D[ebx]
; Сохраняем результат в E (single-precision)
movss [E + ebx * 4], xmm0 ; E[ebx] = результат
; Convert single-precision to double-precision for printf
cvtss2sd xmm0, xmm0 ; Преобразуем в двойную точность
; Подготовка к printf
push ebx ; Индекс
sub esp, 8 ; Выделяем место для double на стеке
movsd [esp], xmm0 ; Сохраняем double-precision float на стеке
push dword [esp+4] ; Пушим старшую часть double
push dword [esp] ; Пушим младшую часть double
push ebx ; Индекс снова
push fmt ; Формат строки
call printf ; Вызов printf
add esp, 16 ; Очистка стека
inc ebx ; Увеличиваем индекс
jmp .loop ; Повторяем цикл
.div_zero_error:
push div_by_zero
call printf
add esp, 4
jmp .end_loop
.end_loop:
xor eax, eax ; Возвращаем 0
mov esp, ebp ; Восстанавливаем указатель стека
pop ebp ; Восстанавливаем указатель базы
ret