Pow реализация для двойного

Я разрабатываю код, который будет использоваться для управления движением, и у меня возникла проблема с функцией pow. Я использую VS2010 в качестве IDE.

Вот моя проблема: у меня есть:

    double p = 100.0000;
    double d = 1000.0000;
    t1 = pow((p/(8.0000*d),1.00/4.000);

При оценке этой последней функции я не получаю лучшего приближения в результате. Я правильно понимаю 7 десятичных цифр, а все последующие цифры - мусор. Я предполагаю, что функция pow только преобразует любую входную переменную в число с плавающей запятой и выполняет расчет.

  1. Я прав?
  2. Если да, то есть ли какой-нибудь код, которым я могу «вдохновиться», чтобы повторно реализовать pow для большей точности?

Изменить: решено.

В конце концов, у меня были проблемы с битами конфигурации FPU, вызванные Direct3D, который использовался платформой OGRE 3D.

Если вы используете OGRE, в графическом интерфейсе конфигурации просто установите «Режим с плавающей запятой = Согласованный».

Если вы используете необработанный Direct3D, при вызове CreateDevice обязательно передайте ему флаг «D3DCREATE_FPU_PRESERVE».

Исходный пост:

Возможно, вы используете библиотеку, которая изменяет точность FPU по умолчанию на одинарную точность. Тогда все операции с плавающей запятой, даже с двойными, будут фактически выполняться как операции с одинарной точностью.

В качестве теста вы можете попробовать вызвать _controlfp( _CW_DEFAULT, 0xffffff ); (необходимо включить ) перед выполнением расчета, чтобы убедиться, что вы получили правильный результат. Это сбросит управляющее слово с плавающей запятой к значениям по умолчанию. Обратите внимание, что при этом будут сброшены и другие настройки, что может вызвать проблемы.

Одной из распространенных библиотек, которая изменяет точность с плавающей запятой, является Direct3D 9 (и, возможно, другие версии): по умолчанию она изменяет FPU на одинарную точность при создании устройства. Если вы его используете, укажите флаг D3DCREATE_FPU_PRESERVE при создании устройства, чтобы предотвратить изменение точности FPU.


person Paulo Sherring    schedule 17.07.2012    source источник
comment
см. прототип pow() в стандартной библиотеке, поставляемой с вашим компилятором. У glibc pow() есть прототип со всеми double. Версия float называется powf().   -  person Aftnix    schedule 17.07.2012
comment
gcc произвел это с точностью до 50 цифр 0,33437015248821100321663379872916266322135925292969   -  person Aftnix    schedule 17.07.2012
comment
Я сделал... Я использую на деле double pow(double, double)... Но, я имею в виду, не находите ли вы очень странным, что функция возвращает ровно 7 десятичных цифр точные результаты, точное количество десятичных цифр что тип поплавка может нести?   -  person Paulo Sherring    schedule 17.07.2012
comment
Excel дает (1/80)^(1/4) == 0,334370152488211, что согласуется как с тем, что Aftnix получил с gcc, так и с тем, что я получил с VS2005. Кроме того, ваш пример плохо вставляется: префикс t1 с двойным и исправьте несбалансированную скобку.   -  person Prashant Kumar    schedule 17.07.2012
comment
Используете ли вы DirectX в своей программе?   -  person interjay    schedule 17.07.2012


Ответы (2)


Возможно, вы используете библиотеку, которая изменяет точность FPU по умолчанию на одинарную точность. Тогда все операции с плавающей запятой, даже на doubles, будут фактически выполняться как операции с одинарной точностью.

В качестве теста вы можете попробовать вызвать _controlfp( _CW_DEFAULT, 0xfffff ); (необходимо включить <float.h>) перед выполнением вычислений, чтобы увидеть, получите ли вы правильный результат. Это сбросит управляющее слово с плавающей запятой к значениям по умолчанию. Обратите внимание, что при этом будут сброшены и другие настройки, что может вызвать проблемы.

Одной из распространенных библиотек, которая изменяет точность с плавающей запятой, является Direct3D 9 (и, возможно, другие версии): по умолчанию она изменяет FPU на одинарную точность при создании устройства. Если вы используете его, укажите флаг D3DCREATE_FPU_PRESERVE при создании устройства, чтобы предотвратить изменение точности FPU.

person interjay    schedule 17.07.2012
comment
Interjay: Большое спасибо! Я использую D3D и наконец-то избавился от этой ужасной проблемы! - person Paulo Sherring; 18.07.2012
comment
Другое дело: я использую OGRE 3D для рендеринга. Я подумал о том, чтобы прочитать статус FPU, изменить его, а затем, после вычислений, изменить его обратно. Но это может быть небезопасно для потоков, так как процессор может быть вытеснен перед его изменением. Что делать: пытаться сделать это через OGRE или попробовать какую-нибудь критическую секцию? Большое спасибо, снова и снова! - person Paulo Sherring; 18.07.2012
comment
Хорошо, только что сделал это через графический интерфейс настройки системы рендеринга Direct3D. Спасибо!!! - person Paulo Sherring; 18.07.2012

Как вы определили, что получаете только 7 цифр точности? Вы печатаете t1 и указываете правильный формат вывода? На моей машине с VS2010 следующий код:

int main()
{
    double p = 100.0000;
    double d = 1000.0000;
    double t1 = pow(p/(8.0000*d),1.00/4.000);

    printf("t1=%.15f\n", t1); // C
    std::cout << "t1=" << std::setprecision(15) << t1 << '\n'; // C++
}

Производит этот вывод:

t1=0.334370152488211
t1=0.334370152488211
person Blastfurnace    schedule 17.07.2012
comment
Я смог определить это, проанализировав результат. Я правильно понял 7 цифр, все остальные цифры расходились с ожидаемыми результатами. Я решил это, используя подсказку Interjay. - person Paulo Sherring; 18.07.2012