May 28th, 2020

Программист - он и в JPL программист

Когда-то я писал, что программисты в USNO применяли для интерполяции классическую формулу Лагранжа, что глупо и неэффективно. Видимо, им лень было читать учебник по численным методам дальше первой страницы.

Теперь напоролся на глюк в коде JPL, с помощью которого читаются файлы эфемерид серии DE (DE200, DE405 и т.п.). Вот кусок их кода (подпрограмма READHD в файле testeph1.f):

read(NRFILE,REC=1)TTL,(NAMS(K),K=1,OLDMAX),SS,NCON,AU,EMRAT,
& ((IPT(I,J),I=1,3),J=1,12),NUMDE,(IPT(I,13),I=1,3),
& (NAMS(J),J=K,NCON),
& (IPT(I,14),I=1,3),
& (IPT(I,15),I=1,3)
Тут из первой (заголовочной) записи читаются сначала имена констант
(NAMS(K),K=1,OLDMAX)
числом OLDMAX (= 400), затем разные другие величины, потом остаток имен констант
(NAMS(J),J=K,NCON)
Так получилось, потому что в старых теориях констант было мало, вот и зарезервировали в файле для них 400 мест. За ними в файле следовали прочие параметры. Когда число констант увеличилось сверх лимита (в DE430, например, их 572), решили формат файла не менять, а константы дописать в конец.

Когда я компилировал это код транслятором gfortran из семейства GCC без оптимизации, все работало как нужно. Но стоило включить оптимизацию O2, как получалась полная фигня: порядок чтения нарушался, элементы массива IPT получали неправильные значения.

Засада была вот в этом элементе списка чтения:
(NAMS(J),J=K,NCON)
Переменная K использовалась как параметр цикла в одном из предыдущих элементов этого списка. Поэтому нельзя ожидать, что по окончании цикла она будет иметь определенное значение. Без оптимизации она действительно сохраняла последнее присвоенное значение. Но оптимизирующий компилятор построил код иначе, что и привело к проблемам. Понятно даже, что именно сделал компилятор: он вычислил начальные и конечные значения параметров всех внутренних циклов до начала выполнения оператора READ, когда переменная K еще не имена конкретного значения. 

Ежу понятно, что программисту надо бы читать описание языка, прежде чем генерить год. Однако в индустрии победил подход Дональда Кнута: "мне лень про это писать, спроси у кого-нибудь, кто знает" (книга Кнута The Joy of TEX изобилует подобными советами). А сейчас еще любят учиться по роликам на Ютубе.

Короче говоря, перепишите это оператор так:
        read(NRFILE,REC=1)TTL,(NAMS(K),K=1,OLDMAX),SS,NCON,AU,EMRAT,
& ((IPT(I,J),I=1,3),J=1,12),NUMDE,(IPT(I,13),I=1,3),
& (NAMS(J),J=OLDMAX+1,NCON),
& (IPT(I,14),I=1,3),
& (IPT(I,15),I=1,3)
и будет вам счастье.

Оригинал этого сообщения находится здесь.