Попытка получить доступ к макросам long double Math и точности long double

Прежде всего, я пытаюсь получить доступ к этим длинным двойным макросам, определенным в Math.h.

/*  Long-double versions of M_E, etc for convenience on Intel where long-
    double is not the same as double.  Define __MATH_LONG_DOUBLE_CONSTANTS
    to make these constants available.                                        */
#if defined __MATH_LONG_DOUBLE_CONSTANTS
#define M_El        0xa.df85458a2bb4a9bp-2L
#define M_LOG2El    0xb.8aa3b295c17f0bcp-3L
#define M_LOG10El   0xd.e5bd8a937287195p-5L
#define M_LN2l      0xb.17217f7d1cf79acp-4L
#define M_LN10l     0x9.35d8dddaaa8ac17p-2L
#define M_PIl       0xc.90fdaa22168c235p-2L
#define M_PI_2l     0xc.90fdaa22168c235p-3L
#define M_PI_4l     0xc.90fdaa22168c235p-4L
#define M_1_PIl     0xa.2f9836e4e44152ap-5L
#define M
#define __MATH_LONG_DOUBLE_CONSTANTS
#import <math.h>
PIl 0xa.2f9836e4e44152ap-4L #define M
#define __MATH_LONG_DOUBLE_CONSTANTS
#import <math.h>
SQRTPIl 0x9.06eba8214db688dp-3L #define M_SQRT2l 0xb.504f333f9de6484p-3L #define M_SQRT1_2l 0xb.504f333f9de6484p-4L #endif /* defined __MATH_LONG_DOUBLE_CONSTANTS */

Я добавил это в начало моего класса:

#define __MATH_LONG_DOUBLE_CONSTANTS
#import <math.h>

Apple говорит, что нужно выставлять макросы, например, M_PIl (long double PI).

Я пытаюсь использовать M_PIl и получаю это сообщение:

использование необъявленного идентификатора «M_PIl», вы имели в виду «P_PID»?

Apple определяет длинные двойные числа как 128-битные числа в GCC4, и я подозреваю, что и в LLDB, и они также говорят, что эти числа могут представлять числа между 3.36210314311209350626 E-4932 и 1.18973149535723176502 E4932. Если моя математика не ошибается, это использует 1 бит для знака мантиссы, 1 бит для знака экспоненты, 16 бит для экспоненты, поэтому мантисса должна иметь 110 бит.

Чтобы проверить это, я делаю

long double pi = acosl(-1.0L);
NSLog(@"%.200Lg", pi);

Вот что написано в консоли

3.14159265358979323851280895940618620443274267017841339111328125 

то есть 64 символа, считая точку.

Тогда я попробую это

NSLog(@"%.200Lg", M_PI);

и это напечатано

3.141592653589793115997963468544185161590576171875

в котором 52 символа.

Первое, что странно, это разница между M_PI и длинной двойной версией с точки зрения символов. Я ожидал в два раза больше символов.

Другое дело, что Apple определяет M_PI как

#define M_PI    3.14159265358979323846264338327950288

это даже отдаленно не похоже на то, что печатается последней командой.

Я также пытался определить PI вручную до 1000 знаков после запятой, просто чтобы посмотреть, что произойдет...

long double pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989L;

и когда я использую NSLog для консоли, результат такой же, как и раньше...

3.141592653589793115997963468544185161590576171875

опять же, число начинает отличаться от 16-го знака после запятой...

Кроме того, когда я делаю

NSInteger x = sizeof(long double);

Я получаю 16 (???)

Не могли бы вы, ребята, объяснить, почему эти несоответствия и как мне получить длинный двойной PI и использовать длинные двойные версии макросов, определенных в Math.h?


person Duck    schedule 31.07.2017    source источник
comment
Apple определяет M_PI таким образом, потому что я считаю, что это double, а не long double...   -  person l'L'l    schedule 31.07.2017
comment
Я знаю, поэтому у них есть длинные двойные версии, но как мне получить к ним доступ, например M_PIl?   -  person Duck    schedule 31.07.2017
comment
Вероятно, вы могли бы сделать NSLog(@"%.200Lg", M_PIl); правильно?   -  person l'L'l    schedule 31.07.2017
comment
Неа. Как я уже сказал, Xcode ноет с use of undeclared identifier 'M_PIl', did you mean 'P_PID'?   -  person Duck    schedule 31.07.2017
comment
Я обновил вопрос с дополнительной информацией   -  person Duck    schedule 31.07.2017
comment
Странно, когда я пробую это в Xcode, я вообще не получаю предупреждений или ошибок... Вот код, который я использовал: >gist.github.com/anonymous/e54e1065c4402e6968f3078ba4e7e73a   -  person l'L'l    schedule 31.07.2017
comment
Я не понимаю ваш код! Вы снова определяете M_PIl и другие макросы? Разве они уже не определены на Math.h?   -  person Duck    schedule 31.07.2017
comment
Переопределение связано с тем, как определение обрабатывается диалектом компилятора. Я знаю, что это странно, но здесь есть сообщение об этом: software.intel.com/en-us/forums/intel-cilk-plus/topic/265759   -  person l'L'l    schedule 31.07.2017
comment
пожалуйста, преобразуйте этот комментарий в ответ, чтобы я мог принять.   -  person Duck    schedule 31.07.2017
comment
Конечно. Если вы читаете пост Сергея и последующие, там больше объяснений...   -  person l'L'l    schedule 31.07.2017


Ответы (2)


В основном это сводится к этому, если я правильно понимаю math.h:

«Существует 20-значное значение, охраняемое #if defined __USE_BSD || defined __USE_XOPEN. Константы в этих группах не разрешены стандартом C для определения в режиме строгого соответствия стандарту.

Хотя первая часть этого не совсем то же самое для macOS, предпосылка такова; второе упомянутое означает, что доступ к #define невозможен на основе используемого диалекта C/C++ и/или режима строгого соответствия стандарту.

https://software.intel.com/en-us/forums/intel-cilk-plus/topic/265759

Включение определений в ваш .m кажется решением, как обсуждалось в комментариях ранее:

#define __MATH_LONG_DOUBLE_CONSTANTS
#import <math.h>
/*  Long-double versions of M_E, etc for convenience on Intel where long-
    double is not the same as double.  Define __MATH_LONG_DOUBLE_CONSTANTS
    to make these constants available.                                        */
#if defined __MATH_LONG_DOUBLE_CONSTANTS
#define M_El        0xa.df85458a2bb4a9bp-2L
#define M_LOG2El    0xb.8aa3b295c17f0bcp-3L
#define M_LOG10El   0xd.e5bd8a937287195p-5L
#define M_LN2l      0xb.17217f7d1cf79acp-4L
#define M_LN10l     0x9.35d8dddaaa8ac17p-2L
#define M_PIl       0xc.90fdaa22168c235p-2L
#define M_PI_2l     0xc.90fdaa22168c235p-3L
#define M_PI_4l     0xc.90fdaa22168c235p-4L
#define M_1_PIl     0xa.2f9836e4e44152ap-5L
#define M_2_PIl     0xa.2f9836e4e44152ap-4L
#define M_2_SQRTPIl 0x9.06eba8214db688dp-3L
#define M_SQRT2l    0xb.504f333f9de6484p-3L
#define M_SQRT1_2l  0xb.504f333f9de6484p-4L
#endif /* defined __MATH_LONG_DOUBLE_CONSTANTS */
person l'L'l    schedule 31.07.2017

Если моя математика не ошибается, это использует 1 бит для знака мантиссы, 1 бит для знака экспоненты, 16 бит для экспоненты, поэтому мантисса должна иметь 110 бит.

Что ж, ваша математика в порядке, ваше предположение неверно. Длинный двойной обычно реализуется с использованием 16 байтов для размещения различных форматов повышенной точности, включая 128-битный IEEE и 80-битный Intel.

Не нужно гадать, как используются эти 80 бит, просто поищите.

Если вы хотите использовать константы #define, скопируйте их из math.h и добавьте в свой код, защищенный #ifndef:

#ifndef M_PIl
 ...
#define M_PIl       0xc.90fdaa22168c235p-2L
 ...
#endif

Теперь выполните тест, используя M_PIl и acosl(), используя формат %LA hex-fp, чтобы вы могли видеть все биты в шестнадцатеричном формате:

long double ld;
ld = acosl(-1.0L);
printf("ld = %LA\n", ld);
ld = M_PIl;
printf("ld = %LA\n", ld);

Ты получишь:

ld = 0XC.90FDAA22168C235P-2
ld = 0XC.90FDAA22168C235P-2

Оба точно такие же, как определение M_PIl с 16 шестнадцатеричными цифрами, 64 бита, в мантиссе.

ХТН

person CRD    schedule 31.07.2017
comment
На самом деле 16-байтовый long double не предназначен для размещения каких-либо различных форматов: он просто предназначен для выравнивания значений, когда они находятся в массиве. - person Ruslan; 15.05.2020