Кроме целых чисел в ассемблере мы можем работать с числами с плавающей точкой. Числа с плавающей точкой или floating-point numbers представляют запись вещественных чисел, в которой число хранится в виде мантиссы и порядка (показателя степени), например,
1.12345 x 102
Для определения чисел с плавающей точкой процессоры x86-64 используют стандарт IEEE 754, согласно которому каждое число содержит ряд компонентов:
бит знака, который указывает, является ли число положительным или отрицательным
мантисса, в примере выше 1.12345
экспонента или показатель степени, в примере 102
это число 2.
Также числа с плавающей точкой характеризуются таким показателем, как десятичные знаки (decimal digits) или еще называют "десятичная точность" (decimal precision). Этот показатель указывает, сколько десятичных знаков может хранить число. Например, в числе 123.45 десятичная точность равна 5 (5 цифр).
Архитектура x86-64 поддерживает три типа данных для работы с числами с плавающей точкой:
Размер | Бит знака | Дробная часть | Экспонента |
32 бита | 1 | 23 | 8 |
64 бита | 1 | 52 | 11 |
80 бит | 1 | 64 | 15 |
Для определения чисел первых двух типов в программе GNU ассемблер GAS предоставляет две директивы:
.float: числа с плавающей точкой одинарной точности по 4 байта. Имеет также псевдоним .single
.double: числа с плавающей точкой двойной точности по 8 байт
Формат типа .float - чисел с плавающей точкой с одинарной точностью использует 23-битную мантисса с дополнением до единицы, 8-битную экспоненту и бит с одним знаком.
Фактически мантисса имеет 24 бита, где старший бит подразумевается, и он всегда равен 1. То есть мантисса выглядит следующим образом:
1.mmmmmmm mmmmmmmm
где mmmmmmm mmmmmmmm
- это 23 бита мантисы. Эти 23 бита представляют беззнаковое число, которые идут после десятичной точкой. То есть мантиса
обычно представляет значение от 1,0 до чуть меньше 2,0.
Для представления значений вне диапазона от 1,0 до 2,0 применяется экспонента. Формат с плавающей точкой возводит 2 в степень, указанную экспонентой, а затем умножает мантиссу на это значение. Показатель степени составляет 8 бит и хранится в формате "excess-127". В этом формате экспонента 0 представляет значение 127 (7Fh), отрицательные показатели представляют собой значения в диапазоне от 0 до 126, а положительные показатели — значения в диапазоне от 128 до 255. Для преобразования экспоненты в формат "excess-127", к значению экспоненты добавляется число 127.
С 24-битной мантиссой можно получить примерно шесть с половиной (десятичных) знаков точности (половина означает, что все первые шесть цифр могут быть в диапазоне от 0 до 9, а седьмая цифра может быть только в диапазоне от 0 до n, где n < 9 и обычно близко к 5). Благодаря этому можно представлить 2±127 значений или примерно 10±38.
Числа с плавающей точкой двойной точности - данные типа .double занимают 64 бита. Первый бит числа также представляет знаковый бит. Однако теперь мантисса занимает 52 бита плюс 1 подразумеваемый бит - старший бит, равный 1. А экспонента занимает 11 бит и использует формат "excess-1023". Это обеспечивает диапазон значений 10±308 и 14,5 знаков точности
Стандарт IEEE для чисел с плавающей токой распознает три специальных нечисловых значения: -infinity (минус бесконечность), +infinity (плюс бесконечность) и NaN (не число). Для каждого из этих специальных чисел поле экспоненты заполняется всеми битами 1. Если в экспоненте все биты 1, а в мантиссе все биты 0, то значение равно бесконечности. Бит знака будет равен 0 для +infinity и 1 для -infinity. Если в экспоненте все биты 1, а в мантиссе не все биты 0, то значение представляет NaN.
В GAS число с плавающей точкой должно начинаться с десятичной цифры, за которой может следовать точка как разделитель целой и дробной частей и некоторое количество десятичных цифр. Если число отрицательное, то перед ним указывается минус (для положительных можно указать знак +, но он необязателен). Например:
1.234 -23.456 0.23 -1.0
Если в целой части только 0, то его можно опустить
.23 (или 0.23) .01567 (или 0.01567)
Также можно использовать экспоненциальную запись, при которой после числа следует буква e или E, за которой может следовать знак (+ или -) и одна или несколько десятичных цифр.
3.75e2 1.1e-1 1.e+4 -123.456e+789 +25.0e0 1.e3
Пример определения переменных чисел с плавающей точкой в программе
.data n1: .float 0.0 n2: .float 2.7 n3: .double 3.14159 n4: .double 0 n5: .double 1.234567e+2 # 123.4567
В дальнейшем мы подробнее рассмотрим инструкции для работы с подобными числами. А пока для небольшого примера рассмотрим следующую задачу:
.globl _start .data num: .double 4.6 .text _start: movq num, %xmm0 # помещаем в регистр %xmm0 число num cvtsd2si %xmm0, %rdi # преобразуем число из %xmm0 в целое число и помещаем в %rdi movq $60, %rax syscall
Здесь первая инструкция - movq
помещает в регистр %xmm0 значение переменной num. Вторая инструкция - cvtsd2si
преобразует значение из xmm0 в целое число,
которое помещается в регистр %rdi. Компиляция и работа программы:
root@Eugene:~/asm# as hello.s -o hello.o root@Eugene:~/asm# ld hello.o -o hello root@Eugene:~/asm# ./hello root@Eugene:~/asm# echo $? 5 root@Eugene:~/asm#
В данном случае видно, что число 4.6 при преобразовании в целое число было округлено до 5.