Файл: А. Б. Шнейвайс основы программирования.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 06.12.2023

Просмотров: 315

Скачиваний: 6

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Результаты работы ts_hugeint q1=digits(i1)=
7;
2** 7=
128
q2=digits(i2)=
15;
2**15=
32768
q4=digits(i4)=
31;
2**31=-2147483648
q8=digits(i8)=
63;
2**63=
0

Почему расчёт 2**7 и 2**15 верен, а 2**31 и 2**63 ошибочен?
huge(i1)=
127=2**q1-1=
127
huge(i2)=
32767=2**q2-1=
32767
huge(i4)=2147483647=2**q4-1=2147483647 2**q4=-2147483648 2**q4-1= 2147483647 2*(2**(q4-1)-1)+1= 2147483647

Почему, -2147483648-1=+2147483647?
======================================================================
2**q4=10000000000000000000000000000000
вычитаем
1=00000000000000000000000000000001
--------------------------------
2**q4-1=01111111111111111111111111111111 2**(q4-1)=01000000000000000000000000000000
вычитаем
1=00000000000000000000000000000001
--------------------------------
2**(q4-1)-1=00111111111111111111111111111111
умножаем на 2=
10
--------------------------------
2(2**(q4-1)-1)=01111111111111111111111111111110
прибавляем
1=00000000000000000000000000000001
--------------------------------
2(2**(q4-1)-1_1)+1=01111111111111111111111111111111
======================================================================
HUGE от целого аргумента kind(i)
digits(i)
huge(i)
2*(2**(digits(i)-1)+1 1
7 127 127 2
15 32767 32767 4
31 2147483647 2147483647 8
63 9223372036854775807
-1
======================================================================
Почему результаты расчёта одного и того же по формуле 2**q8-1

и по huge(i8) различны, хотя, на первый взгляд, должны быть одинаковы?
q8=
63
qo=
63 2*(2**(q8-1)-1)+1=
-1 2*(2**(qo-1)-1)+1=
9223372036854775807 2**qo=1000000000000000000000000000000000000000000000000000000000000000 2**qo= -9223372036854775808 412

Пояснения к результатам работы ts_hugeint.
1. Первые четыре выполняемых оператора получают количество дво- ичных цифр, соответствующих модельному представлению целых чисел. Напомним, что результат, возвращаемый функцией digits по умолчанию, имеет тип integer(4).
2. Значения, получаемые по формулам 2**q1=128 и 2**q2=32768,
свободно размещаются в integer(4) и вычисляются верно (см. пер- вые две строки файла-результата ts_hugeint.res).
3. Однако, 2**q4 — отрицательно (хотя абсолютная величина чис- ла получена верно). Значение 2**31 для записи в четырёхбайтовую ячейку требует её самый старший бит (31-ый), который ФОРТРАН
трактует как знак целого отрицательного числа, записанного в до- полнительном коде. Поэтому неудивительно, что на типе integer(4)
значение 2**31 равно -2147483648.
Тем не менее формула 2**q4-1 даёт верный результат и для типа integer(4), хотя пользуется при этом абсолютно неверным значени- ем 2**31=-2147483648. Почему так происходит, видно из вывода соответствующих операндов в двоичном виде.
4. 2**q8: тип значений 2 и q8 — integer(4). Поэтому и тип резуль- тата — integer(4). Однако, в отличие от случая q4=31, значение q8=63. Запись чисел всегда ориентирована на правую границу по- ля. Поэтому в поле, отводимое для типа integer(4) попадают лишь
32 младших двоичных разряда числа 2**63 (так что w8=0). Вычи- тая из четырёхбайтового нуля единицу, получаем -1 того же типа integer(4). Умножая её на 2 получаем -2. И, наконец, прибавляя к
-2 единицу, получаем -1.
Для правильного представления результата операции 2**63 необ- ходимо использовать тип integer(8)
413


15.6.2
Семейство real
В случае семейства real эффект переполнения при расчёте наибольше- го числа возникает, если запрограммировать выражение b e
max непосред- ственно. Для типа real(4) число 2.0 128
= +Infinity. Для программиро- вания формулу (1 − b
−p
) ∗ b e
max удобно переписать в виде:
b ∗ (1 − b
−p
) ∗ b e
max
−1
program ts_hugereal; implicit none real( 4) r4, h4; real( 8) r8, h8
real(10) rX, hX; real(16) rH, hH
integer emax emax=maxexponent(r4)
write(*,’("emax=maxexponent(r4)=",i4)’) emax write(*,’("
2.0**emax=",e15.7)’) 2.0**emax h4=2*(1-2.0_4
** (-digits(r4))) * 2.0_4
** (maxexponent(r4)-1)
h8=2*(1-2.0_8
** (-digits(r8))) * 2.0_8
** (maxexponent(r8)-1)
hX=2*(1-2.0_10 ** (-digits(rX))) * 2.0_10 ** (maxexponent(rX)-1)
hH=2*(1-2.0_16 ** (-digits(rX))) * 2.0_16 ** (maxexponent(rH)-1)
write(*,’("m=kind(x)",2x,"digits(x)",10x,"huge(x)",7x,&
&"2*(1-2.0_m**(-digits(r)))*"/44x,"2.0_m**(maxexponent(r)-1)")’)
write(*,’(i4,i12,e26.7,e26.7)’) kind(r4), digits(r4), huge(r4), h4
write(*,’(i4,i12,e26.14e4,e26.14e4)’) kind(r8), digits(r8), huge(r8), h8
write(*,’(i4,i12,e26.14e4,e26.14e4)’) kind(rX), digits(rX), huge(rX), hX
write(*,’(i4,i12,e26.14e4,e26.14e4)’) kind(rH), digits(rH), huge(rH), hH
end program ts_hugereal emax=maxexponent(r4)= 128 2.0**emax=
Infinity m=kind(x)
digits(x)
huge(x)
2*(1-2.0_m**(-digits(r)))*
2.0_m**(maxexponent(r)-1)
4 24 0.3402823E+39 0.3402823E+39 8
53 0.17976931348623E+0309 0.17976931348623E+0309 10 64 0.11897314953572E+4933 0.11897314953572E+4933 16 113 0.11897314953572E+4933 0.11897314953572E+4933 414

15.7
Функция precision(x)
— находит десятичную точность представления данного (количество де- сятичных цифр, которое соответствует модели его типа и параметра раз- новидности).
program ts_precision; implicit none real(4)
x4
real(8)
x8
real(10) xX
!real(16) xH
write(*,*)
write(*,’(" PRECISION только для семейств REAL или COMPLEX:")’)
write(*,*)
write(*,’(" precision( x4)=",i4)’) precision( x4)
write(*,’(" precision( x8)=",i4)’) precision( x8)
write(*,’(" precision( xX)=",i4)’) precision( xX)
!write(*,’(" precision( xH)=",i4)’) precision( xH)
write(*,*)
write(*,’(" precision( ( 3.0_4,
0.1_4))=",i4)’) precision( (3.0_4, 0.1_4))
write(*,’(" precision( ( 3.0_8,
0.2_8))=",i4)’) precision( (3.0_8, 0.2_8))
write(*,’(" precision( (3.0_10, 0.3_10))=",i4)’) precision((3.0_10,0.3_10))
!write(*,’(" precision( (3.0_16, 0.3_16))=",i4)’) precision((3.0_16,0.3_16))
end program ts_precision
PRECISION только для семейств REAL или COMPLEX:
precision( x4)=
6
precision( x8)=
15
precision( xX)=
18
precision( ( 3.0_4,
0.1_4))=
6
precision( ( 3.0_8,
0.2_8))=
15
precision( (3.0_10, 0.3_10))=
18 415

15.8
Функция range(x)
— находит значение диапазона показателя степени десятки для модели данного того же типа и того же параметра типа, что и аргумент.
program ts_range; implicit none integer(1) i1; integer(2) i2; integer(4) i4; integer(8) i8
real(4) x4;
real(8) x8;
real(10) xX; complex y write(*,’("RANGE для целых:")’)
write(*,’("range(i)",10x,"huge(i)",10x,"int(lg(huge(real(i))))")’)
write(*,’(i5,i25,i17)’) range(i1), huge(i1), int(log10(real(huge(i1))))
write(*,’(i5,i25,i17)’) range(i2), huge(i2), int(log10(real(huge(i2))))
write(*,’(i5,i25,i17)’) range(i4), huge(i4), int(log10(real(huge(i4))))
write(*,’(i5,i25,i17)’) range(i8), huge(i8), int(log10(real(huge(i8))))
write(*,’("RANGE для комплексного:",i3)’) range(y)
write(*,’("RANGE для вещественных:")’)
write(*,’("m=kind(x)",1x,"range(x)",4x,"huge(x)",2x,"[lg(huge(x))]",&
& 2x, "maxexponent(x)",2x,&
&"[maxexponent(x)*",/&
&66x,"lg(2.0_m)
]")’)
write(*,’(i5,i10,e15.3e2,i8,i17,i17)’) kind(x4),range(x4), huge(x4),&
& int(log10(huge(x4))), maxexponent(x4),int(maxexponent(x4)*log10(2.0_4))
write(*,’(i5,i10,e15.3e3,i8,i17,i17)’) kind(x8),range(x8), huge(x8),&
& int(log10(huge(x8))), maxexponent(x8),int(maxexponent(x8)*log10(2.0_8))
write(*,’(i5,i10,e15.3e4,i8,i17,i17)’) kind(xX),range(xX), huge(xX),&
& int(log10(huge(xX))), maxexponent(xX), int(maxexponent(xX)*log10(2.0_10))
write(*,’("m=kind(x)",1x,"range(x)",4x,"tiny(x)",2x,"[lg(tiny(x))]",&
& 2x, "minexponent(x)",2x,&
&"[minexponent(x)*",/&
&66x,"lg(2.0_m)
]")’)
write(*,’(i5,i10,e15.3e2,i8,i17,i17)’) kind(x4),range(x4), tiny(x4),&
& int(log10(tiny(x4))), minexponent(x4),int(minexponent(x4)*log10(2.0_4))
write(*,’(i5,i10,e15.3e3,i8,i17,i17)’) kind(x8),range(x8), tiny(x8),&
& int(log10(tiny(x8))), minexponent(x8),int(minexponent(x8)*log10(2.0_8))
write(*,’(i5,i10,e15.3e4,i8,i17,i17)’) kind(xX),range(xX), tiny(xX),&
& int(log10(tiny(xX))), minexponent(xX), int(minexponent(xX)*log10(2.0_10))
end
416


RANGE для целых:
range(i)
huge(i)
int(lg(huge(real(i))))
2 127 2
4 32767 4
9 2147483647 9
18 9223372036854775807 18
RANGE для комплексного: 37
RANGE для вещественных:
m=kind(x) range(x)
huge(x)
[lg(huge(x))]
maxexponent(x)
[maxexponent(x)*
lg(2.0_m)
]
4 37 0.340E+39 38 128 38 8
307 0.180E+309 308 1024 308 10 4931 0.119E+4933 4932 16384 4932
1   ...   15   16   17   18   19   20   21   22   23

m=kind(x) range(x)
tiny(x)
[lg(tiny(x))]
minexponent(x)
[minexponent(x)*
lg(2.0_m)
]
4 37 0.118E-37
-37
-125
-37 8
307 0.223E-307
-307
-1021
-307 10 4931 0.336E-4931
-4931
-16381
-4931 417


15.9
Функция bit_size(i)
Функция bit_size(i) находит число битов в модели данных той же раз- новидности целого типа, что и аргумент i.
program ts_bit_size implicit none integer(1) i1; integer(2) i2; integer(4) i4; integer(8) i8
write(*,’("bit_size(i1)=",i4)’) bit_size(i1)
write(*,’("bit_size(i2)=",i4)’) bit_size(i2)
write(*,’("bit_size(i4)=",i4)’) bit_size(i4)
write(*,’("bit_size(i8)=",i4)’) bit_size(i8)
end bit_size(i1)=
8
bit_size(i2)=
16
bit_size(i4)=
32
bit_size(i8)=
64 418

16
Приложение VII. Некоторые опции gfortran
16.1
Опции изменения правила умолчания
Напомним, что правило умолчания ФОРТРАНа полагает переменные,
имена которых начинаются с букв i, j, k, l, m, и n, предназначенными для работы со значениями типа integer, а переменные, имена которых начинаются с любой другой буквы — со значениями типа real. Так что слова integer и real — те самые служебные слова, которые определяют тип переменной по умолчанию. Подчеркнём, ещё раз — не real(4) или integer(4), а именно integer и real, т.е. без явного указания размера области памяти, отводимой под данные этих типов. При этом изначально считалось, что оба этих типа четырёхбайтовые по умолчанию.
Однако оказалось, что для задач расчётного характера в значитель- ной мере могут быть востребованы типы real(8), real(10), real(16).
Возникла необходимость замены описания real на real(8), real(16) или даже вообще на real(mp) (mp — именованная константа).
На первый взгляд наиболее практично — переопределить правило умолчания на новый тип данных, указав соответствующую опцию ком- пилятора. Тогда обеспечивается 100%-ая гарантия такого переопределе- ния во всех единицах компиляции, входящих в проект, при отсутствии каких-либо изменений в исходных текстах.
16.1.1
Опция -fdefault-real-8
Опция -fdefault-real-8 переопределяет правило умолчания для типа real c четырёхбайтового на восьмибайтовый.
В таком случае описание double precision соответственно трактуется как шестнадцатибайтовое.
419

Рассмотрим, например, программу program treal0; implicit none real, parameter :: c=1.3;
real x, pi real(4) y;
real*4 z double precision q, qa, qb;
real(8) t, s;
real(16) u write(*,*) ’
c=’,c; x=1.7
write(*,*) ’
x=’,x y=2.7;
write(*,*) ’
y=’,y z=3.7;
write(*,*) ’
z=’,z t=4.7;
write(*,*) ’
t=’,t s=4.7_8;
write(*,*) ’
s=’,s u=4.7_16; write(*,*) ’
u=’,u pi=4*atan(1.0)
write(*,*) ’ pi=’,pi write(*,*) ’
sin(pi)=’,sin(pi)
write(*,*) ’
cos(pi)=’,cos(pi)
write(*,*) ’sin(pi/4)-sqrt(2.0)/2=’,sin(pi/4)-sqrt(2.0)/2
q=pi qa=4*atan(1.0_8);
qb=4*atan(1.0d0)
write(*,*) ’ q=’,q write(*,’(a,e42.35,1x,a,e10.2)’) ’ qa=4*atan(1.0_8)=’,qa,’sin(qa)=’,sin(qa)
write(*,’(a,e42.35,1x,a,e10.2)’) ’ qb=4*atan(1.0d0)=’,qb,’sin(qb)=’,sin(qb)
end
Результат работы её исполнимого кода после gfortran treal0.f95 и
./a.out таков:
c=
1.29999995
x=
1.70000005
y=
2.70000005
z=
3.70000005
! Если не оговорено особо, то по умолчанию t=
4.6999998092651367
! запись 4.7 есть константа одинарной точности.
s=
4.7000000000000002
u=
4.70000000000000000000000000000000015
pi=
3.14159274
sin(pi)=
-8.74227766E-08
cos(pi)=
-1.00000000
sin(pi/4)-sqrt(2.0)/2=
0.00000000
q=
3.1415927410125732
qa=4*atan(1.0_8)= 0.31415926535897931159979634685441852E+01 sin(qa)=
0.12E-15
qb=4*atan(1.0d0)= 0.31415926535897931159979634685441852E+01 sin(qb)=
0.12E-15
• Вывод результата происходит под управлением списка вывода (на что указывает вторая звёздочка в операторе write). Поэтому ко- личество выводимых значащих цифр данных вещественных типов всегда соответствует разновидности типа, что позволяет по выводу результата проследить изменение трактовки компилятором правила умолчания ФОРТРАНа.
420


• Можно заметить, что хотя t описана восьмибайтовой (её значе- ние, действительно, состоит из соответствующего разрядности чис- ла цифр), тем не менее, в напечатанном значении к верным отно- сится лишь старшая половина цифр.
• Причина — форма записи константы 4.7 есть форма записи четы- рёхбайтовой константы. Так что 4.7 перевелось в двоичную систему счисления с погрешностью округления порядка 10
−7
и результат пе- ревода с этой погрешностью был преобразован к формату двоичного представления типа real(8).
• Аналогично, переменной q типа double precision (что при неизме- нённом правиле умолчания эквивалентно real(8)) присвоено значе- ние четырёхбайтовой переменной pi, погрешность машинного пред- ставления которой по порядку величины такая же, как и у 4.7. По- этому, хоть мы и видим у q шестнадцать цифр, к верным следует отнести лишь старшие семь-восемь.
• Работа встроенных математических функций даёт ожидаемый для типа real результат: pi вычислено с одинарной (четырёхбайтовой)
точностью; значение sin(pi) по сути дела равно погрешности ма- шинного представления значения pi; cos(pi) = −1.0 c погрешно- стью не хуже 10
−8
и , вероятно, с не худшей погрешностью вычис- ляются sin(pi/4) и sqrt(2.0)/2.
Однако, после gfortran -fdefault-real-8 treal0.f95 и ./a.out получим:
c=
1.3000000000000000
<--= Работа опции x=
1.7000000000000000
<--= Работа опции y=
2.70000005
Опция не отработала z=
3.70000005
Опция не отработала t=
4.7000000000000002
<--= Работа опции s=
4.7000000000000002
u=
4.70000000000000000000000000000000015
pi=
3.1415926535897931
sin(pi)=
1.2246467991473532E-016
cos(pi)=
-1.0000000000000000
sin(pi/4)-sqrt(2.0)/2=
-1.1102230246251565E-016
q=
3.14159265358979311599796346854418516
qa=4*atan(1.0_8)= 0.31415926535897931159979634685441852E+01 sin(qa)=
0.12E-15
qb=4*atan(1.0d0)= 0.31415926535897932384626433832795028E+01 sin(qb)=
0.87E-34 421

• Описания real(4) или real*4 для опции -fdefault-real-8 не экви- валентны real (они не являются описаниями по умолчанию: в них явно указано количество байт). Поэтому значения переменных y и z остались четырёхбайтовыми.
• На первый взгляд описатель real (разновидность типа по умолча- нию) более лаконичен (по сравнению с real(4) или real(8)) и в то же время посредством -fdefault-real-8 просто переводится в режим умолчания real(8), причём опять-таки без какого-либо изменения исходного текста, что наиболее практично.
• Форма записи константы 4.7 не изменилась, но работа опции обес- печила погрешность округления, соответствующую типу real(8).
• Тип double precisin переменной q после действия опции стал трак- товаться как real(16) — по умолчанию вывелось 35 десятичных цифр, из которых верны лишь старшие 16: ведь шестнадцатибай- товой q присвоено значение восьмибайтовой pi.
• Для корректного присваивания переменной qb значения π исполь- зуем оператор qb=4*atan(1.0D0). Имя atan — родовое. Через него реализуется механизм перегрузки функций, подставляющий вместо atan соответствующее типу аргумента специфическое имя функции. Опция -fdefault-real-8 сопоставляет константе 1.0 тип real(8), а константе 1.0d0 — тип real(16).
• В современном ФОРТРАНе 1.0d0 и 1.0_8 обозначают одну и ту же вещественную константу. Опция же -fdefault-real-8 трактует синтаксис их записи по разному.
В операторе qa=4*atan(1.0_8) явно указано, что аргумент типа real(8). Поэтому для расчёта arctg вызывается функция со специ- фическим именем datan, получающая значение типа real(8). Для выполнения расчёта с четверной — необходимо константу записать в виде 1.0d0, т.е. именно в форме D
• Вывод. -fdefault-real-8 обеспечивает переход на четверную точность при описаннии констант посредством их записи только в форме D, а при описании типа переменных —
только посредством double precision.
422