Как заставить gcc на SUN вычислять числа с плавающей точкой так же, как в Linux - PullRequest
0 голосов
/ 27 апреля 2010

У меня есть проект, где я должен выполнить некоторые математические вычисления с двойными переменными. Проблема в том, что я получаю разные результаты на SUN Solaris 9 и Linux. Существует множество способов (объясняемых здесь и на других форумах), как заставить Linux работать как Sun, но не наоборот. Я не могу коснуться кода Linux, поэтому я могу изменить только СОЛНЦЕ. Есть ли способ заставить SUN вести себя как Linux?

Код, который я запускаю (скомпилируйте с gcc в обеих системах):

int hash_func(char *long_id)          
{                                 
    double      product, lnum, gold;
    while (*long_id)
        lnum = lnum * 10.0 + (*long_id++ - '0');
    printf("lnum  => %20.20f\n", lnum);
    lnum = lnum * 10.0E-8;
    printf("lnum  => %20.20f\n", lnum);
    gold = 0.6125423371582974;
    product = lnum * gold;
    printf("product => %20.20f\n", product);
    ...
}

, если ввод 339886769243483

вывод в Linux:

lnum  => 339886769243**483**.00000000000000000000

lnum  => 33988676.9243**4829473495483398**

product => 20819503.600158**59827399253845**

В режиме СОЛНЦЕ:

lnum => 339886769243483.00000000000000000000

lnum => 33988676.92434830218553543091

product = 20819503.600158**60199928283691**

Примечание: Результат не всегда отличается, более того, в большинстве случаев он один и тот же. Только 10 15-значных чисел из 60000 имеют эту проблему.

Пожалуйста, помогите !!!

Ответы [ 3 ]

6 голосов
/ 27 апреля 2010

Реальный ответ здесь - другой вопрос: почему вы думаете, что вам это нужно? Может быть лучший способ выполнить то, что вы пытаетесь сделать, который не зависит от сложных деталей платформы с плавающей точкой. Сказав это ...

К сожалению, вы не можете изменить код Linux, так как на самом деле результаты Linux являются здесь недостаточными. Результаты SUN настолько хороши, насколько это возможно: они правильно округлены; каждое умножение дает уникальный (в данном случае) C double, наиболее близкий к результату. Напротив, первое умножение Linux не дает правильно округленный результат.

Ваши результаты по Linux получены из 32-битной системы на оборудовании x86, верно? Результаты, которые вы показываете, соответствуют и, вероятно, вызваны явлением «двойного округления»: результат первого умножения сначала округляется до 64-битной точности (точности, используемой внутренним процессором Intel x87 FPU), а затем повторно основанный на обычной 53-битной точности двойного. Большую часть времени (примерно в 1999 году из 2000 или около того в среднем) этот двойной раунд имеет тот же эффект, что и одиночный раунд до 53-битной точности, но иногда он может давать другой результат, и это то, что вы ' видите здесь.

Как вы говорите, есть способы исправить результаты Linux так, чтобы они совпадали с результатами Solaris: один из них заключается в использовании соответствующих флагов компилятора для принудительного использования инструкций SSE2 для операций с плавающей запятой, если это возможно. В последнем выпуске gcc версии 4.5 также исправлена ​​разница с помощью нового флага -fexcess-precision, хотя это исправление может повлиять на производительность, если не использовать SSE2.

[Редактировать: после нескольких перечитываний руководств по gcc, ветки списка рассылки gcc-patches по адресу http://gcc.gnu.org/ml/gcc-patches/2008-11/msg00105.html, и связанного с ним отчета об ошибке gcc , мне все еще не ясно, будет ли использоваться -fexcess-precision=standard фактически исключает двойное округление в системах x87; Я думаю, что ответ зависит от значения FLT_EVAL_METHOD. У меня нет 32-битной машины Linux / x86, чтобы проверить это.]

Но я не знаю, как бы вы исправили результаты Solaris, чтобы они соответствовали результатам Linux, и я не уверен, почему вы захотите: вы будете делать результаты Solaris менее точными, а не делать Результаты Linux более точны.

[Редактировать: у Кафа есть хорошее предложение здесь. На Solaris попробуйте сознательно использовать long double для промежуточных результатов, а затем принудительно вернуться к удвоению. Если все сделано правильно, это должно воспроизвести эффект двойного округления, который вы видите в Linux.]

См. Отличную статью Дэвида Моннио Подводные камни проверки вычислений с плавающей точкой для хорошего объяснения двойного округления. Это важно прочитать после статьи Гольдберга, упомянутой в предыдущем ответе.

3 голосов
/ 27 апреля 2010

Две вещи:

  • вам нужно прочитать это: http://docs.sun.com/source/806-3568/ncg_goldberg.html

  • вам необходимо решить, какие требования предъявляются к точности чисел в вашем приложении

2 голосов
/ 27 апреля 2010

Эти значения отличаются менее чем на одну часть в 2 52 . Но число с двойной точностью имеет всего 52 бита за точку отсчета. Таким образом, они отличаются только в последнюю очередь. Может случиться так, что одна машина не всегда правильно округляет, но вы делаете два умножения, поэтому в любом случае ответ будет иметь такую ​​большую ошибку.

...