Зачем определять PI = 4*ATAN(1.d0)

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

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

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


person ccook    schedule 28.01.2010    source источник
comment
В качестве альтернативы я почти ожидал увидеть PI = 3.1415926535... и т.д.   -  person ccook    schedule 29.01.2010
comment
Я нашел превосходный математический ответ для этого уравнения на сайте Math Stackoverflow math.stackexchange.com/questions/1211722/   -  person lindsaymacvean    schedule 18.09.2015
comment
Если вы используете PI = 3.1415926535..., вы должны добавить суффикс типа данных, чтобы получить что-либо кроме реальной точности по умолчанию. Поскольку вы использовали двойную точность f66, это будет суффикс D0.   -  person tim18    schedule 22.03.2018
comment
Примечание: современный Фортран не требует DATAN(): ATAN() является псевдонимом соответствующих версий с одинарной и двойной точностью в зависимости от аргумента.   -  person jvriesem    schedule 29.03.2018
comment
современный Фортран, ты меня там потерял;)   -  person ccook    schedule 11.04.2018
comment
Современный Фортран — это Фортран ›= Фортран 2003   -  person Biggsy    schedule 23.08.2018
comment
Упомянутая здесь функция была введена в Fortran 77. За последние 30 лет оставшаяся ситуация, когда вы будете использовать datan, довольно надуманная и вряд ли будет полезной.   -  person tim18    schedule 06.09.2018


Ответы (6)


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

person John Gietzen    schedule 28.01.2010
comment
Но будьте осторожны, если вы будете следовать этому подходу - не все компиляторы и лежащие в их основе математические библиотеки одинаковы в результатах, которые они возвращают для триггерных функций чисел с плавающей запятой, особенно в критических точках этих функций. Недавно было долгое обсуждение этого на comp.lang.fortran, где тусуется большинство экспертов Fortran. Их вывод - укажите константу pi = 3,14159... (достаточно цифр для требуемой точности, а затем несколько для безопасности). - person High Performance Mark; 30.01.2010
comment
Высокая оценка производительности: было бы неплохо, если бы вы могли дать ссылку на тему comp.lang.fortran, которую вы упомянули! - person jvriesem; 13.09.2013
comment
К сожалению, некоторые языки делают все возможное, чтобы гарантировать, что значение pi, возвращаемое этим подходом, не совпадает с тем значением, которое используется при вычислении периода триггерных функций. - person supercat; 03.06.2014
comment
@supercat: Есть идеи, почему? Или вы можете процитировать его, чтобы я мог задать его как новый вопрос? :-) - person jvriesem; 29.03.2018
comment
@jvriesem: если кто-то пытается вычислить, например. sin(31416), правильное математическое значение должно быть sin(31416 минус (10000 раз математическое пи), а не sin(31416 - 10000 раз ближайшее значение с плавающей запятой к математическому пи). - person supercat; 29.03.2018
comment
@jvriesem: Лично я сомневаюсь, что очень много ситуаций, когда кто-то, например, звонит. sin(x), когда x находится за пределами диапазона +/-pi/2, не будет в равной степени доволен синусом любого значения, чье ближайшее представление равно x. Тем не менее, среда выполнения языка Java добавляет дополнительный код к своим реализациям триггерных функций, которые, когда их просят вычислить sin(x) для x около, например, 2π сначала вычитает точно представимое значение, близкое к 2π, а затем вычитает разницу между этим значением и математическим 2π. - person supercat; 29.03.2018
comment
@jvriesem: Если представить систему с плавающей запятой с пятью десятичными знаками точности, которую просят вычислить sin (3,1416), она может (отметив, что π составляет около 3,1415 + 0,000092653 + 0,00000000058979), вычесть 3,1415 из значения (получив 0,0001), а затем вычесть из этого 0,000092653, что даст 0,000007347, а затем вычесть из этого 0,00000000058979, что даст 0,0000073464, из которых затем будет взят синус, что даст -0,0000073464. - person supercat; 29.03.2018
comment
@jvriesem: этот результат был бы пятизначным значением, наиболее близким к математическому синусу точного значения 3,1416, но если 3,1416 является результатом вычисления, значение которого, если бы оно было точно вычислено без округления, могло бы быть где-то между 3,14155 и 3,14165, тогда идеально вычисленный синус идеально вычисленного значения может быть где-то между -0,000057346 и 0,000042653, и любое значение в этом диапазоне будет по существу таким же хорошим, как и любое другое. - person supercat; 29.03.2018

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

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

PI=DACOS(-1.D0)
PI=2.D0*DASIN(1.D0)
person jason    schedule 28.01.2010
comment
Обратите внимание, что в Fortran 77 и более поздних версиях общие имена ACOS и ASIN предпочтительнее конкретных имен DACOS и DASIN. - person Vladimir F; 26.07.2017

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

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

atan(x) = x^1/1 - x^3/3 + x^5/5 - x^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.

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

pi = 16*atan(1/5) - 4*atan(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). тангенс х (sin х / cos х) = 1, атан х (1 / тангенс х) = 1.

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

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

person Justin    schedule 17.11.2013
comment
Я не понимаю, как вы переходите от atan x (1/tan x) = 1 к atan (x) = 1. - person lindsaymacvean; 18.09.2015
comment
обратите внимание, что с формулой pi = 16*atan(1/5) - 4*atan(1/239) численно не является хорошим выбором. И 1/5, и 1/239 не могут быть точно представлены с помощью чисел с плавающей запятой. Следовательно, атан уже внесет ошибки из-за этих приближений с плавающей запятой. - person kvantour; 21.12.2019

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

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

person John Feminella    schedule 28.01.2010

В этом вопросе есть нечто большее, чем кажется на первый взгляд. Почему 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.bnbn-1.. .b0 x 2м. Если обратная тригонометрическая функция дает наилучшее числовое бинарное приближение для своего аргумента, мы не хотим терять точность при умножении. Для этого мы должны умножать только на степени 2.

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

Примечание. это видно в двоичном файле IEEE-75464 представление (наиболее распространенная форма 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 )

Аргумент реализации: на ЦП Intel atan2 является частью инструкции x86. устанавливается как FPATAN, а другая обратная тригонометрическая функция получается из atan2. Потенциальным выводом может быть:

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

Это можно увидеть в ассемблере этих инструкций (см. здесь). С этой целью я бы поспорил с использованием:

π = 4 arctan(1)

Примечание. это нечеткий аргумент. Я уверен, что есть люди с лучшим мнением по этому поводу.
Интересно почитать о FPATAN: Как реализован ли arctan?, тригонометрические инструкции x87

Аргумент Fortran: почему мы должны аппроксимировать π следующим образом:

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

Ответ содержится в стандарте Fortran. В стандарте никогда указано, что 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
person kvantour    schedule 21.03.2018
comment
Наряду с аргументом sin(pi) /= 0 я всегда не хотел предполагать, что acos(-1d0) будет столь же точным, как 4*atan(1d0), хотя это может быть, в зависимости от реализации. - person tim18; 22.03.2018
comment
Я полагаю, что существует проверенный алгоритм для sin() для получения правильно округленного значения для больших аргументов, хотя это требует значительного дополнительного времени выполнения. Я не видел ссылку. Некоторые библиотеки IBM предоставили вам это по умолчанию. Любая высококачественная реализация будет включать в себя некоторую симуляцию дополнительной точности, которая позволяет избежать значительного снижения точности до аргументов, настолько больших, насколько это может быть полезно на практике, скажем +-20 Pi, или (меньшей) точки, в которой внутренняя реализация m387 резко меняется от возврат точного значения для возврата аргумента. - person tim18; 06.09.2018
comment
Кстати, внутренняя константа Pi в прошивке m387 рекламируется как несущая 66-битную точность. Собственно, в реализации нет ничего особенного; правильно округленное 66-битное значение точности имеет 2 младших нулевых бита. Высококачественный компилятор C даст вам это значение для длинной двойной константы с 21 цифрой. - person tim18; 06.09.2018

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

person Andrew McGregor    schedule 28.01.2010
comment
На самом деле это очень распространенный способ установки значения PI - не только в Фортране, но и в других языках. (См. комментарии выше.) - person jvriesem; 13.09.2013