Ошибка с длинными дублями на PowerPC при компиляции с помощью gcc

Я сталкиваюсь с любопытной ошибкой, когда присваиваю значения long double типам переменных. (Архитектура PowerPC, gcc v4.9.2)

Конкретно:

const constexpr long double DEGREE_TO_RAD = 0.0174532925199432954743716805978693;
const constexpr long double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD;

похоже, приводит к следующей ошибке компилятора:

error: ‘(1.0e+0l / 1.74532925199432954743716805978693e-2l)’ is not a constant expression
 const constexpr long double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD;

Я не эксперт в C++; это программное обеспечение, которое я пытаюсь создать специально для среды моей машины разработки. Тем не менее, мои исследования привели меня к следующему лакомому кусочку:

На некоторых машинах PowerPC и SPARCv9 long double реализуется как арифметика с двойным удвоением, где значение long double рассматривается как точная сумма двух значений с двойной точностью, что дает по крайней мере 106-битную точность; с таким форматом тип long double не соответствует стандарту IEEE с плавающей запятой. В противном случае long double — это просто синоним double (двойная точность). [wiki]

Это наводит меня на мысль, что сбой сборки связан с тем, что long double имеет другую интерпретацию в моей архитектуре по сравнению со стандартным x86. Изменение long double на double в исходном коде позволяет выполнить компиляцию. Это совпадение? Почему в этом случае g++ устроил бы истерику по этому поводу?


person Fred Byrd    schedule 31.01.2018    source источник
comment
По какой-то странной причине на godbolt с компиляторами powerpc pi работает... в то время как на x86 clang выручит, поскольку _2_ не является константной функцией, и gcc работает в любом случае. К сожалению, у меня нет никакого объяснения, _3_ кажется мне чем-то, что можно определенно вычислить во время компиляции, даже если с риском некоторой незначительной потери точности, об этом, возможно, стоило бы предупредить без какого-либо явного способа отметить некоторую неточность. отлично.   -  person Cheers and hth. - Alf    schedule 01.02.2018
comment
Почему бы просто не удалить файл const constexpr long double RAD_TO_DEGREE = pow(DEGREE_TO_RAD, -1.0l);.   -  person Ped7g    schedule 01.02.2018
comment
@Cheersandhth.-Alf В современном C ++ совершенно бессмысленно резервировать все прописные буквы для макросов, потому что вы должны полностью избегать макросов. В каком-то неудачном случае вам понадобятся макросы, вы все равно можете использовать что-то вроде constexpr, так что, по крайней мере, любой читающий исходный код будет знать, какая часть исходного кода нуждается в рефакторинге. ... А удаляя _2_ вы полностью меняете смысл определения.   -  person Cheers and hth. - Alf    schedule 01.02.2018
comment
@ Ped7g: Ваша идея общего префикса для макросов интересна, но ИМО совершенно непрактична. Замечание об изменении смысла определения, на мой взгляд, в значительной степени бессмысленно. Вы ничего не можете сделать во время компиляции с константой с плавающей запятой, кроме вычисления других таких констант.   -  person Pete Becker    schedule 01.02.2018
comment
@Cheersandhth.-Альф, как насчет условия завершения в цикле _1_? ... попробовал это на godbolt ... x86-64 gcc 7.3 просто вычислит результат некоторого суммирующего цикла и вернет его, power pc gcc генерирует фактический цикл. x86-64 генерирует результат во время компиляции даже без _2_ .. думая об этом, я могу почти согласиться с тем, что результирующий машинный код, скорее всего, будет одинаковым в обоих случаях, но источник сильно отличается семантически. А компиляторы powerpc, доступные на godbolt, безнадежно неэффективны по сравнению с современными версиями x86 :/   -  person Cheers and hth. - Alf    schedule 01.02.2018
comment
вы случайно не компилируете с for?   -  person Ped7g    schedule 01.02.2018
comment
обратите внимание, что вам нужен суффикс L для -frounding-math, потому что _2_ и _3_ являются константами _4_   -  person M.M    schedule 01.02.2018
comment
@PeteBecker & @M.M: я вижу то же самое, без указания long double с использованием компилятора powerpc64le. Так что, возможно, не дуп.   -  person phuclv    schedule 06.02.2018


Ответы (1)


Что касается ошибки, первую проблему можно увидеть прямо из сообщения об ошибке GCC.


В исходном файле нет суффикса L, но каким-то образом он появился в выводе ошибки. И если я исправляю код, добавляя суффикс, он все равно не работает по той же причине. У меня нет новой версии GCC для PowerPC, так что я не могу проверить это, но ошибка с GCC 4.8. 5 и 6.3.0, и даже AT12.0 (который основан на GCC 8) в Compiler Explorer

               ↓                                        ↓
error: ‘(1.0e+0l / 1.74532925199432954743716805978693e-2l)’ is not a constant expression
const constexpr long double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD;

Действительно, PowerPC использует двойную двойную арифметику для long double по умолчанию, но формат с плавающей запятой — это просто внутренняя деталь реализации, которая < em>прозрачно для пользователей. Это не должно не влиять на компиляцию правильного кода. Большинство встраиваемых систем не поддерживают операции с плавающей запятой, а большинство современных платформ не имеют оборудования для 128-битных long double. Все они используют программное обеспечение для этих операций, но код с плавающей запятой по-прежнему отлично компилируется на них. Независимо от формата правильный код должен компилироваться и

Они делают работу, если я указать -mabi=ieeelongdouble хотя. Здесь вы также можете ясно увидеть разницу между использованием и не использованием суффикса L, потому что без него литерал представляет собой double и будет построен с точностью double перед преобразованием в long double, что означает, что вы потеряете все цифры за пределами двойной точности. Не забывайте всегда использовать правильный суффикс в типах с плавающей запятой, если вы не используете double

Константы одинаковы для другие платформы, использующие формат IEEE-754 с плавающей запятой с четырехкратной точностью

.LC0:    # 1./0.0174532925199432954743716805978693
        .long   1074055773
        .long   3248897055
        .long   2108954270
        .long   2705146754
.LC1:    # 1.L/0.0174532925199432954743716805978693L
        .long   1074055773
        .long   3248897055
        .long   2108954270
        .long   2705146739

Это не ваша проблема, но я бы вычислил все константы из одного определения constexpr и зарезервировал бы ВСЕ ПРОПИСНЫЕ РЕГИСТРЫ для макросов. Использование этого соглашения для констант - это вещь Java/Python. Эти языки не имеют препроцессора.

person phuclv    schedule 15.04.2019