Зачем определять PI = 4 * ATAN (1.d0) - PullRequest
53 голосов
/ 29 января 2010

Какова мотивация для определения PI как

PI=4.D0*DATAN(1.D0)

в коде Фортрана 77? Я понимаю, как это работает, но в чем причина?

Ответы [ 6 ]

63 голосов
/ 29 января 2010

Этот стиль гарантирует, что максимальная точность, доступная в ЛЮБОЙ архитектуре, используется при назначении значения для PI.

14 голосов
/ 29 января 2010

Потому что у Фортрана нет встроенной константы для PI. Но вместо того, чтобы вводить число вручную и, возможно, совершить ошибку или не получить максимально возможную точность в данной реализации, библиотека позволяет рассчитать результат для вас и гарантирует, что ни один из этих недостатков не произойдет.

Это эквивалентно, и вы иногда их тоже увидите:

PI=DACOS(-1.D0)
PI=2.D0*DASIN(1.D0)
13 голосов
/ 17 ноября 2013

Полагаю, это потому, что это самая короткая серия на пи. Это также означает, что это САМОЕ ТОЧНОЕ.

Ряд Григория-Лейбница (4/1 - 4/3 + 4/5 - 4/7 ...) равен пи.

атан (х) = х ^ 1/1 - х ^ 3/3 + х ^ 5/5 - х ^ 7/7 ...

Итак, атан (1) = 1/1 - 1/3 + 1/5 - 1/7 + 1/9 ... 4 * атан (1) = 4/1 - 4/3 + 4/5 - 4/7 + 4/9 ...

Это равно ряду Грегори-Лейбница и, следовательно, равно пи, примерно 3.1415926535 8979323846 2643383279 5028841971 69399373510.

Другой способ использовать atan и найти число пи:

пи = 16 * атан (1/5) - 4 * атан (1/239), но я думаю, что это сложнее.

Надеюсь, это поможет!

(Если честно, я думаю, что серия Грегори-Лейбница была основана на атане, а не 4 * атан (1) на основе серии Грегори-Лейбница. Другими словами, НАСТОЯЩЕЕ доказательство это:

sin ^ 2 x + cos ^ 2 x = 1 [Теорема] Если x = pi / 4 радиана, sin ^ 2 x = cos ^ 2 x или sin ^ 2 x = cos ^ 2 x = 1 / 2.

Тогда sin x = cos x = 1 / (корень 2). tan x (sin x / cos x) = 1, atan x (1 / tan x) = 1.

Так что, если atan (x) = 1, x = pi / 4 и atan (1) = pi / 4. Наконец, 4 * атан (1) = пи.)

Пожалуйста, не загружайте меня комментариями - я все еще несовершеннолетний.

9 голосов
/ 29 января 2010

Это потому, что это точный способ вычисления pi с произвольной точностью. Вы можете просто продолжить выполнение функции для получения большей и большей точности и остановиться в любой точке, чтобы получить приближение.

В отличие от этого, указав pi в качестве константы, вы получите в точности такую ​​же точность, как и изначально, что может не подходить для высоконаучных или математических приложений (поскольку Фортран часто используется с).

4 голосов
/ 21 марта 2018

В этом вопросе есть нечто большее, чем кажется на первый взгляд. Почему 4 arctan(1)? Почему не любое другое представление, такое как 3 arccos(1/2)?

Это попытается найти ответ путем исключения.

математическое вступление: При использовании обратных тригонометрических функций , таких как arccos, arcsin и arctan , можно легко вычислить π в различных способы:

π = 4 arctan(1) = arccos(-1) = 2 arcsin(1) = 3 arccos(1/2) = 6 arcsin(1/2)
  = 3 arcsin(sqrt(3)/2) = 4 arcsin(sqrt(2)/2) = ...

Существует множество других точных алгебраических выражений для тригонометрических значений , которые можно использовать здесь.

аргумент с плавающей точкой 1: хорошо известно, что конечное двоичное представление с плавающей точкой не может представлять все действительные числа. Некоторые примеры таких чисел 1/3, 0.97, π, sqrt(2), .... Для этого мы должны исключить любые математические вычисления π, в которых аргумент для обратных тригонометрических функций не может быть представлен численно. Это оставляет нам аргументы -1,-1/2,0,1/2 и 1.

π = 4 arctan(1) = 2 arcsin(1)
   = 3 arccos(1/2) = 6 arcsin(1/2)
   = 2 arccos(0)
   = 3/2 arccos(-1/2) = -6 arcsin(-1/2)
   = -4 arctan(-1) = arccos(-1) = -2 arcsin(-1)

аргумент с плавающей точкой 2: в двоичном представлении число представляется как 0.b n b n-1 .. .b 0 x 2 m . Если обратная тригонометрическая функция придумала наилучшее числовое двоичное приближение для своего аргумента, мы не хотим терять точность при умножении. Для этого нам нужно умножить только на степени 2.

π = 4 arctan(1) = 2 arcsin(1)
  = 2 arccos(0)
  = -4 arctan(-1) = arccos(-1) = -2 arcsin(-1)

примечание: это видно в представлении IEEE-754 binary64 (наиболее распространенная форма DOUBLE PRECISION или kind=REAL64). Там у нас есть

write(*,'(F26.20)') 4.0d0*atan(1.0d0) -> "    3.14159265358979311600"
write(*,'(F26.20)') 3.0d0*acos(0.5d0) -> "    3.14159265358979356009"

Эта разница отсутствует в двоичном файле IEEE-75432 (наиболее распространенная форма REAL или kind=REAL32) и двоичном коде IEEE-754128 (наиболее распространенная форма kind=REAL128)

нечеткий аргумент реализации: с этого момента все немного зависит от реализации обратных тригонометрических функций. Иногда arccos и arcsin являются производными от atan2 и atan2 как

ACOS(x) = ATAN2(SQRT(1-x*x),1)
ASIN(x) = ATAN2(1,SQRT(1-x*x))

или более конкретно с числовой точки зрения:

ACOS(x) = ATAN2(SQRT((1+x)*(1-x)),1)
ASIN(x) = ATAN2(1,SQRT((1+x)*(1-x)))

Кроме того, atan2 является частью x86 Инструкции, установленной как FPATAN, в то время как другие - нет. С этой целью я бы поспорил использование:

π = 4 arctan(1)

над всеми остальными.

Примечание: это нечеткий аргумент. Я уверен, что есть люди с лучшим мнением по этому поводу.

Аргумент Фортрана: почему мы должны аппроксимировать π как:

integer, parameter :: sp = selected_real_kind(6, 37)
integer, parameter :: dp = selected_real_kind(15, 307)
integer, parameter :: qp = selected_real_kind(33, 4931)

real(kind=sp), parameter :: pi_sp = 4.0_sp*atan2(1.0_sp,1.0_sp)
real(kind=dp), parameter :: pi_dp = 4.0_dp*atan2(1.0_dp,1.0_dp)
real(kind=qp), parameter :: pi_qp = 4.0_qp*atan2(1.0_qp,1.0_qp)

а не:

real(kind=sp), parameter :: pi_sp = 3.14159265358979323846264338327950288_sp
real(kind=dp), parameter :: pi_dp = 3.14159265358979323846264338327950288_dp
real(kind=qp), parameter :: pi_qp = 3.14159265358979323846264338327950288_qp

Ответ лежит в стандарте Фортрана . Стандарт никогда гласит, что REAL любого вида должен представлять IEEE-754 число с плавающей запятой . Представление REAL зависит от процессора. Это подразумевает, что я мог бы запросить selected_real_kind(33, 4931) и ожидать получить двоичное число 128 с плавающей запятой , но я мог бы получить kind, которое представляет плавающую точку с гораздо более высокой точностью. Может быть, 100 цифр, кто знает. В этом случае моя строка чисел выше короткая! Нельзя использовать этот просто чтобы быть уверенным? Даже этот файл может быть слишком коротким!

интересный факт: sin(pi) is never zero

write(*,'(F17.11)') sin(pi_sp) => "   -0.00000008742"
write(*,'(F26.20)') sin(pi_dp) => "    0.00000000000000012246"
write(*,'(F44.38)') sin(pi_qp) => "    0.00000000000000000000000000000000008672"

что понимается как:

pi = 4 ATAN2(1,1) = π + δ
SIN(pi) = SIN(pi - π) = SIN(δ) ≈ δ

program print_pi
! use iso_fortran_env, sp=>real32, dp=>real64, qp=>real128

  integer, parameter :: sp = selected_real_kind(6, 37)
  integer, parameter :: dp = selected_real_kind(15, 307)
  integer, parameter :: qp = selected_real_kind(33, 4931)

  real(kind=sp), parameter :: pi_sp = 3.14159265358979323846264338327950288_sp
  real(kind=dp), parameter :: pi_dp = 3.14159265358979323846264338327950288_dp
  real(kind=qp), parameter :: pi_qp = 3.14159265358979323846264338327950288_qp

  write(*,'("SP "A17)') "3.14159265358..."
  write(*,'(F17.11)') pi_sp
  write(*,'(F17.11)')        acos(-1.0_sp)
  write(*,'(F17.11)') 2.0_sp*asin( 1.0_sp)
  write(*,'(F17.11)') 4.0_sp*atan2(1.0_sp,1.0_sp)
  write(*,'(F17.11)') 3.0_sp*acos(0.5_sp)
  write(*,'(F17.11)') 6.0_sp*asin(0.5_sp)

  write(*,'("DP "A26)') "3.14159265358979323846..."
  write(*,'(F26.20)') pi_dp
  write(*,'(F26.20)')        acos(-1.0_dp)
  write(*,'(F26.20)') 2.0_dp*asin( 1.0_dp)
  write(*,'(F26.20)') 4.0_dp*atan2(1.0_dp,1.0_dp)
  write(*,'(F26.20)') 3.0_dp*acos(0.5_dp)
  write(*,'(F26.20)') 6.0_dp*asin(0.5_dp)

  write(*,'("QP "A44)') "3.14159265358979323846264338327950288419..."
  write(*,'(F44.38)') pi_qp
  write(*,'(F44.38)')        acos(-1.0_qp)
  write(*,'(F44.38)') 2.0_qp*asin( 1.0_qp)
  write(*,'(F44.38)') 4.0_qp*atan2(1.0_qp,1.0_qp)
  write(*,'(F44.38)') 3.0_qp*acos(0.5_qp)
  write(*,'(F44.38)') 6.0_qp*asin(0.5_qp)

  write(*,'(F17.11)') sin(pi_sp)
  write(*,'(F26.20)') sin(pi_dp)
  write(*,'(F44.38)') sin(pi_qp)


end program print_pi
0 голосов
/ 29 января 2010

Звучит ужасно, как обходной путь для ошибки компилятора. Или же это может быть из-за того, что эта конкретная программа точна, и поэтому программист гарантировал это.

...