Совместимость FORTRAN и C — могут ли REAL(4) быть слишком маленькими?

У нас были какие-то странные сбои в некотором коде Intel FORTRAN, и я в конце концов отследил строку до:

L_F = EXP(-L_B2*L_BETASQ*L_DS)

Где значение -L_B2*L_BETASQ*L_DS равно примерно -230. Как это бывает, EXP(-230) оценивается примерно в 1e-100. Во всех других известных случаях L_DS намного меньше, в результате чего наименьший (известный) возврат от EXP составляет около 1e-50, что не вызывает ошибки.

Как только FORTRAN оценит предложение EXP(-230), вы получите:

forrtl: severe (157): Program Exception - access violation
Image              PC        Routine            Line      Source

Но никакой другой информации.

Исключение 157 обычно связано с функциональной совместимостью, и вы не можете выполнить отладку в EXP на FORTRAN, поскольку он не может найти конкретный файл .c, что предположительно означает, что EXP реализован в C (что я нахожу удивительным).

Моя гипотеза состоит в том, что FORTRAN реализовал EXP в C, но интерфейс не может преобразовать числа с плавающей запятой, которые меньше 1e-100, в REAL(4). Поскольку я ранее считал, что числа с плавающей запятой и REAL(4) идентичны по байтам, я не могу подтвердить эту гипотезу — и нигде не могу найти ничего об этом.

Прежде чем я закрою эту ошибку, может ли кто-нибудь подтвердить или опровергнуть мою гипотезу или предоставить мне другую?

С наилучшими пожеланиями,

Майк

РЕДАКТИРОВАТЬ: я собираюсь отметить этот вопрос как ответ, так как High Performance Mark ответил на непосредственный вопрос.

Моя гипотеза, к сожалению, неверна - я пытался поймать проблему, сделав это:

L_ARG = L_B2*L_BETASQ*L_DS

IF (L_ARG .GT. 230.0) THEN
    L_F = 0.0
ELSE
    L_F = EXP(-L_ARG)
ENDIF

К сожалению, теперь исключение (очевидно) происходит в L_ARG .GT. пункт 230.0. Это либо означает, что отладка в режиме Release хуже, чем я думал, либо это какая-то «сохраненная» ошибка с плавающей запятой (см. invalid-operation-when-inputting-float-to-a-stringstream">Недопустимая операция с плавающей запятой при вводе float в строковый поток).


person Mike Sadler    schedule 15.01.2013    source источник
comment
Я указываю на 3-й абзац моего ответа. Включите проверку границ массива во время выполнения и проверку аргументов процедуры во время компиляции.   -  person High Performance Mark    schedule 15.01.2013
comment
Отладочный код выпуска почти никогда не работает из-за оптимизации хранения регистров и того факта, что фактические инструкции, реализующие последовательные операторы, могут быть перемешаны, и, таким образом, связь ПК ‹-› исходной строки полностью нарушается.   -  person Hristo Iliev    schedule 16.01.2013
comment
Ваш код 32-битный или 64-битный? Если код 32-битный, используется ли математика SSE? Потому что есть некоторые различия в том, как обрабатываются потери значимости в x87 и SSE. Если ваша основная программа написана на Фортране, попробуйте с параметром /Qftz на ifort.   -  person Hristo Iliev    schedule 16.01.2013
comment
Он скомпилирован 32-битным (но моя собственная машина 64-битная). Основная подпрограмма написана на FORTRAN, но она так часто появляется и выходит из C++, что это не соответствует действительности. Как узнать, какая математика используется? Я как раз собираюсь поэкспериментировать с параметром /Qftz (похоже, это именно то, что мне нужно изменить).   -  person Mike Sadler    schedule 17.01.2013


Ответы (1)


Фортран (обязательно) ничего не реализовал на C. Реализация стандартных встроенных функций зависит от компилятора; обычно обнаруживают, что реализации вызывают libm или один из его родственников. С точки зрения Intel (или любого другого автора компилятора) это имеет смысл, напишите одну надежную и быструю реализацию exp на любом языке, который вам нравится, и вызовите его из Fortran, C, Ada, COBOL и всех других языков, которые вы знаете. когда-либо слышал. Может быть, даже имеет смысл написать его на C. Поэтому часть вашей гипотезы может оказаться верной.

Однако, если вы явно не пишете код C и код Fortran и не создаете из него один двоичный файл, на самом деле не происходит никакого взаимодействия (в стандартном смысле Fortran), все грязные детали этого скрыты (или должны быть) скрыты от вас; компилятор должен генерировать правильные вызовы любых библиотек, которые он использует для реализации exp, и получать возвращаемые значения, какими бы они ни были, включая NaN и подобные.

Конечно, значение exp(-230) равно 0.00000000 для 4-байтового числа, но я не вижу причин, по которым программа на Фортране, использующая библиотеку, написанную на C, должна вызвать нарушение прав доступа из-за того, что она сталкивается с этими числами. Я думаю, что гораздо более вероятно, что у вас есть ошибка в другом месте вашей программы, возможно, при попытке доступа к элементу массива за пределами массива, и что ваша среда выполнения не может идентифицировать его в правильном месте в исходном коде. Это не редкость.

ИЗМЕНИТЬ

Я написал этот материал о совместимости до того, как (повторно) прочитал вопрос. Теперь, когда вы пояснили, что используете функции взаимодействия, это может быть интересно или полезно...

Вы, конечно, не можете полагаться на то, что real(4) вашего Fortran и float вашего C идентичны; это очень вероятно, но не точно. Большинство современных компиляторов Фортрана (включая компиляторы Intel) используют параметры типового типа, которые соответствуют количеству байтов в их представлении, поэтому код 4 указывает, что вы имеете дело с 4-байтовым вещественным числом, которое на процессоре, совместимом с IEEE-754, должно быть таким же, как C float. Стандарты Fortran не требуют никакого соответствия между этими типами параметров типа и количеством байтов, используемых для представления числа. Всегда стоит проверить документацию вашего компилятора или провести некоторые тесты.

Если вас беспокоит интероперабельность, вам, вероятно, следует использовать встроенные функции Fortran. Например, если вы

use :: iso_c_binding

у вас есть ряд констант, включая C_FLOAT, которые можно использовать следующим образом:

real(C_FLOAT) :: a_fortran_float

Если ваш компилятор Fortran поддерживает это, то a_fortran_float должно соответствовать числу с плавающей запятой C на сопутствующем процессоре. Этот последний термин остается несколько неопределенным, на практике компиляторы из одной и той же стабильной версии всегда кажутся компаньонами, для разных конюшен иногда да, иногда нет. Компиляторы Intel Fortran и C и C++ кажутся компаньонами в необходимом смысле. Меня не удивит, если я узнаю, что компиляторы Intel Fortran и MS C++ не работают вместе.

Мои смутные воспоминания о C включают в себя неуверенность в том, что float стандартизировано, и в этом случае вы не можете быть уверены, не тестируя и не читая документацию, что у вас действительно есть 4-байтовое число с плавающей запятой одинарной точности IEEE на той стороне. вашего взаимодействия либо.

person High Performance Mark    schedule 15.01.2013
comment
Спасибо, Марк - все интересное. Двоичный файл является на самом деле смешанным двоичным файлом FORTRAN-C++ с использованием VS2010 (то есть VC++ с Intel FORTRAN), но я не счел уместным упоминать об этом. Я могу выполнить код только в режиме Release, так как проблема не возникает в режиме Debug, но в режиме Release довольно непреклонен тот факт, что проблема вызвана вызовом EXP. Тот факт, что это было изменено в нашем новом рабочем коде, также предполагает (но не доказывает), что причиной проблемы является EXP(-230). Я постараюсь привести отдельный пример... - person Mike Sadler; 15.01.2013