Как GCC компилирует 80-битный 10-байтовый float __float80 на x86_64?

Согласно одному из слайдов в видео Что такое видео Creel, "Современная сборка x64 4: Типы данных" (ссылка на слайд),

Примечание: real10 используется только с x87 FPU, в настоящее время его в значительной степени игнорируют, но он обеспечивает потрясающую точность!

Он говорит,

"Real10 используется только с модулем вычислений с плавающей запятой x87. [...] Интересно, какой огромный прирост точности он дает. Вы как бы получаете удар по производительности с таким приростом, потому что вы не можете использовать real10 с SSE, упакованные инструкции в стиле SIMD. Но это довольно интересно, потому что, если вам нужна дополнительная точность, вы можете перейти на FPU в стиле x87. Сейчас он почти не используется вообще."

Однако я погуглил и увидел, что GCC поддерживает __float80 и __float128.

Рассчитывается ли __float80 в GCC на x87? Или он использует SIMD, как и другие операции с плавающей запятой? А как насчет __float128?


person Evan Carroll    schedule 18.04.2018    source источник


Ответы (2)


документы GCC для дополнительных плавающих типов:

ISO/IEC TS 18661-3:2015 определяет поддержку C для дополнительных плавающих типов _Floatn и _Floatnx.

... В настоящее время GCC не поддерживает _Float128x ни на каких системах.

Я думаю, что _Float128x - это двоичный файл IEEE128, то есть настоящее 128-битное число с плавающей запятой с огромным диапазоном показателей. См. http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1691.pdf.


__float80, очевидно, является 10-байтовым типом x87. В x86-64 SysV ABI это то же самое, что и long double; оба имеют 16-байтовое выравнивание в этом ABI.

__float80 доступен для целей i386, x86_64 и IA-64 и поддерживает 80-битный (XFmode) плавающий тип. Это псевдоним имени типа _Float64x для этих целей.


Я думаю, что __float128 - это тип повышенной точности, использующий SSE2, предположительно двойной двойной формат с удвоенной шириной мантиссы, но теми же ограничениями экспоненты, что и 64-битный double. (т.е. меньший диапазон показателей, чем __float80)

В i386, x86_64 и... __float128 является псевдонимом для _Float128.

Вероятно, это те же самые doubledouble, которые дает вам gcc с __float128. Или может быть это 128-битная программа с плавающей запятой


Проводник компилятора Godbolt для gcc7.3 -O3 (то же, что и gcc4.6, очевидно, эти типы не новы)

//long double add_ld(long double x) { return x+x; }  // same as __float80
__float80 add80(__float80 x) { return x+x; }

    fld     TBYTE PTR [rsp+8]    # arg on the stack
    fadd    st, st(0)
    ret                          # and returned in st(0)


__float128 add128(__float128 x) { return x+x; }

          # IDK why not movapd or better movaps, silly compiler
    movdqa  xmm1, xmm0           # x arg in xmm0
    sub     rsp, 8               # align the stack
    call    __addtf3             # args in xmm0, xmm1
    add     rsp, 8
    ret                          # return value in xmm0, I assume


int size80 = sizeof(__float80);    // 16
int sizeld = sizeof(long double);  // 16

int size128 = sizeof(__float128);  // 16

Таким образом, gcc вызывает функцию libgcc для __float128 добавления, не встраивая приращение в экспоненту или что-то в этом роде.

person Peter Cordes    schedule 18.04.2018
comment
__float128 не double double, по крайней мере, на x86_64 это программная реализация ieee binary128. На компьютерах Power long double традиционно был типом double-double, но от него отходят. - person Marc Glisse; 19.04.2018
comment
@MarcGlisse: мне было трудно найти определения _Float128 и _Float128x. Итак, _Float128 — это IEEE binary128, но что такое _Float128x? Не стесняйтесь редактировать этот ответ, если у вас есть время. - person Peter Cordes; 19.04.2018
comment
_Float128x должен быть расширенным форматом, связанным с IEEE binary128. т.е. он должен иметь экспоненциальную ширину не ниже двоичного256 (т.е. emax ≥ 65535) и точность между двоичным128 и двоичным256 (в частности, p цифр ≥ 128). См. §3.7 IEEE 754-2019 (тот же раздел в редакции 2008 г.) для получения подробной информации об этих расширенных форматах. Соответствие между _Float128x и _Float128 почти такое же, как между __float80 и double в gcc, за исключением явно сохраненного старшего бита мантиссы в __float80, что несовместимо с IEEE. - person Ruslan; 26.10.2019

Я нашел ответ здесь

__float80 доступен для целей i386, x86_64 и IA-64 и поддерживает 80-битный (XFmode) плавающий тип. Это псевдоним имени типа _Float64x для этих целей.

Просмотрев XFmode,

Режим «Extended Floating» представляет собой расширенное число с плавающей запятой IEEE. Этот режим имеет только 80 значимых битов (десять байтов). Некоторые процессоры требуют, чтобы такие числа были дополнены до двенадцати байтов, другие — до шестнадцати; этот режим используется для любого.

Все еще не полностью убежденный, я собрал что-то простое

int main () {
    __float80 a = 1.445839898;
    return 1;
}

С помощью Радара я его сбросил,

0x00000652      db2dc8000000   fld xword [0x00000720]
0x00000658      db7df0         fstp xword [local_10h]

Я считаю, что fld и fstp являются частью набора инструкций x87. Так что это правда, что он используется для __float80 10-байтового числа с плавающей запятой, однако на __float128 я получаю

0x000005fe      660f6f05aa00.  movdqa xmm0, xmmword [0x000006b0]
0x00000606      0f2945f0       movaps xmmword [local_10h], xmm0

Итак, мы видим здесь, что мы используем SIMD xmmword

person Evan Carroll    schedule 18.04.2018
comment
Он может использовать SIMD для перемещения данных, но это не значит, что он будет использовать его для реальных операций. - person Marc Glisse; 19.04.2018