что не так с синусами?


#1

столкнулся с решением задачи в которой требуется найти синус.

смотрю таблицу синусов: sin(60)=0.866025

считаю на калькуляторе sin(60)=0,86602540378443864676372317075294

считаю в программе writeln(sin(60));

и получаю ответ: -0.304810621102217

паскаль как то по другому считает синусы???


#2

Мировой стандарт - хранить углы в радианах а не градусах. Синусы в паскале .Net -овские, поэтому они тоже по этому стандарту работают. Как и всё остальное из System.Math..


#3

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


#4

На сколько я знаю - школьных учебников по PascalABC.Net нету. Все для более старых паскалей. А документация есть, и на msdn, и, вроде, даже в основной справке паскаля.


#5

я про учебники по математике

вот что есть в хелпе

function Sin(x: real): real; 
        Возвращает синус числа x

и никаких упоминаний в чем оно считает. Естественно человек привыкший все считать в градусах ожидает тут увидеть ответ в градусах.

и как это теперь перевести в градусы кстати?


#6

Длина окружности - 360 градусов, они же 2π.

Радианы = градусы*π/180

В моём учебнике по алгебре всё с радианами. Не знаю где вы градусы нашли. Только если вы из 9-ого/8-ого/7-ого и ниже класса такое может быть. Там ещё про радианы не рассказывают. Привыкайте к радианам.


#7

Оно есть в документации. Просто читаете не там.

Там не ответ в радианах, а аргумент функции…

Вот документация от прямых конкурентов.

Вот трубопаскакаль.

Вот GNU Pascal.

А можно мне хоть один ЯП, в котором в тригонометрические функции принимают не радианы?..

А насчет того, как это перевести в градусы, предлагаю погуглить.


#8

Тут есть две готовые функции туда и обратно: DegToRad(x: real): real; RadToDeg(x: real): real; Переводят градусы в радианы и наоборот.

begin
  Sin(DegToRad(30)).Println;
  RadToDeg(ArcSin(0.5)).Println
end.

#9

я хотел бы это читать не на МСДН, а в хелпе паскаля

и о чудо в отличии от ПаскальАБС у них с хелпом все в порядке :slight_smile: может стоит поучиться у конкурентов?


#10

Гхм. Вот интересно - а где граница между “надо делать справку для метода, он наш”, и “не надо делать справку, он же из .NET”? Люди, которые тут появляются, просят то справку на Windows Forms, то на System.Threading.Task, то на System.Math… Серьезно, я бы на месте разрабов справку, за пределами внесенных в “стандартный” паскаль модификаций вроде ReadlnInteger, выкинул целиком и полностью, оставив ссылку на русский (переведенный) MSDN.

Не стоит. Они не разрабатывают язык на основе крупной внешней платформы с огромной функциональностью и количеством написанного кода. Им справку написать (самую полную, какая есть) - неделя работы максимум.


#11

и тут мы возвращаемся к вопросу – для кого PascalABC? На кого он позиционируется? На крупных софтверных разработчиков? Сомневаюсь что такие им пользуются. Или на школьников и студентотв? Много школьников полезут на МСДН, а если полезут поймут что там написано? Хотя если данная версия паскаля сделана чисто по приколу то тогда да вы правы и все нормально.


#12

Поправили документацию


#13

В программировании, как и математике, углы всегда задаются в радианной мере. В градусах - это только в школьной математике, да и то не всегда. В любом языке программирования мера для углов всегда радианная. Даже в Экселе, где готовые функции для ячеек. Поэтому всегда можно вывести “на чистую воду” кривую реализацию арифметики, если 4*arctan(1) не совпадает с принятым значением числа “пи”.


#14

Есть такой код:

{Если X — вещественное число, то синусом X в математическом анализе называется синус угла, радианная* мера которого равна X.

Определение тригонометрических функций через ряды Тейлора:

sin X = X - X^3/3! + x^5/5! - x^7/7! + X^9/9! . . . = Σ [(-1)ᶰ]*X^(2N+1)] / (2N + 1)! n=0

или CORDIC.

xPI=355/113; } begin var X := 0.2345; var e := 0.00001; var N := 1; var S := X; var H := X;

while (ABS(H) > e) do begin//repeat H := H * (-1) * X ** 2 / (N + 1) / (N + 2); S += H; N += 2; end;//until not (ABS(H) > e);

writeln(S); var SS:=SIN(x); writeln(SS); writeln(SS-S:0:15); SS:=355/113; Writeln(SS:2:15); Writeln(Pi); Writeln(SS-Pi:0:15); end.

Вопрос: автоматическое присваивание выбирает оптимальный тип или первый подходящий по вместимости? Как можно улучшить работу с дробными в данном случае?

Спасибо.


#15

Код надо выделять так:

```
код
```

Это называется operator implicit и работает не только на присвоение.
Всегда выбирается оптимальное преобразование. Если одинаково хороших вариантов несколько - компилятор даст ошибку.


#16

Вы о чем? В PascalABC.NЕТ для вещественных данных всегда выбирается тип real. Если нужен single - это следует указать явно.


#17

“Типы real и double являются синонимами” (8 байт)

Да, четыре байта (single) вполне достаточно для точности 10^-5.

Спасибо.


#18

Вы правы, но современные процессоры аппаратно обрабатывают данные с плавающей точкой, как 64-битные по стандарту IEEE- 754, обеспечивая точность в 15—17 десятичных цифр и масштабы в диапазоне 1e−308 … 1e+308, так что использование single связано с аппаратной эмуляцией и существенно замедляет программу.


#19

А вот это вы сами додумали:

// запускать по Shift+F9, отключив в настройках генерацию отладочной информации
begin
  var lc := 1000000;
  var glc := 10000;
  
  var sw_sa := new System.Diagnostics.Stopwatch;
  var sw_ss := new System.Diagnostics.Stopwatch;
  var sw_sm := new System.Diagnostics.Stopwatch;
  var sw_sd := new System.Diagnostics.Stopwatch;
  
  var sw_da := new System.Diagnostics.Stopwatch;
  var sw_ds := new System.Diagnostics.Stopwatch;
  var sw_dm := new System.Diagnostics.Stopwatch;
  var sw_dd := new System.Diagnostics.Stopwatch;
  
  var s1,s2,s3: single;
  var d1,d2,d3: real;
  
  (d1, d2) := Random2;
  (s1, s2) := (d1, d2);
  
  for var i := 1 to glc do
  begin
    
    
    
    sw_sa.Start;
    loop lc do s3 := s1+s2;
    sw_sa.Stop;
    
    sw_ss.Start;
    loop lc do s3 := s1-s2;
    sw_ss.Stop;
    
    sw_sm.Start;
    loop lc do s3 := s1*s2;
    sw_sm.Stop;
    
    sw_sd.Start;
    loop lc do s3 := s1/s2;
    sw_sd.Stop;
    
    
    
    sw_da.Start;
    loop lc do d3 := d1+d2;
    sw_da.Stop;
    
    sw_ds.Start;
    loop lc do d3 := d1-d2;
    sw_ds.Stop;
    
    sw_dm.Start;
    loop lc do d3 := d1*d2;
    sw_dm.Stop;
    
    sw_dd.Start;
    loop lc do d3 := d1/d2;
    sw_dd.Stop;
    
    
    
    System.Console.Title := $'{i/glc:P} done';
  end;
  
  writeln('single:');
  writeln($'  + : {sw_sa.Elapsed}');
  writeln($'  - : {sw_ss.Elapsed}');
  writeln($'  * : {sw_sm.Elapsed}');
  writeln($'  / : {sw_sd.Elapsed}');
  writeln;
  writeln('real:');
  writeln($'  + : {sw_da.Elapsed}');
  writeln($'  - : {sw_ds.Elapsed}');
  writeln($'  * : {sw_dm.Elapsed}');
  writeln($'  / : {sw_dd.Elapsed}');
  
  readln
end.

У меня 64-битная система и разницы между real и single нет:

single:
  + : 00:00:07.3306535
  - : 00:00:07.0395896
  * : 00:00:07.0080677
  / : 00:00:07.0020689

real:
  + : 00:00:07.0129721
  - : 00:00:06.9809689
  * : 00:00:06.9897568
  / : 00:00:06.9920366

Ну и результаты на ещё 1 менее продвинутом процессоре (тоже 64-битном):

single:
  + : 00:00:19.0832407
  - : 00:00:22.0746419
  * : 00:00:13.1602147
  / : 00:00:14.2028236

real:
  + : 00:00:14.1337665
  - : 00:00:18.4587784
  * : 00:00:14.0638083
  / : 00:00:13.1556001

#20

А Вы смотрели, во что компилируется real и single на уровне процессора? Возможно, в .NET разница и отсутствует, но я точно знаю, что под DOS она была. И как раз ее объясняли аппаратной реализацией.