Что вижу - о том пою (aragont) wrote,
Что вижу - о том пою
aragont

Category:

Программисткий беспредел

2.0*2.0 примерно равно 4.0, а точнее нам и не надо
(старый программисткий анекдот)

Почти все помнят со школы, что на ноль делить нельзя. Кто-то из старших товарищей, даже рассказывал байку про немецкие механические счётные машины "Рейнметалл":
Это устройство было похоже на арифмометр "Феликс", но с электромотором, который со изрядным шумом раскручивал счетные барабаны. В отличие от "Феликса" операции умножения и деления были автоматизированы. Если в процессе вычислений происходило деление на ноль, то барабаны со страшным стуком клинило, после чего приходилось вызывать механиков для починки.

На самом деле, иногда делить на ноль можно. Например, если мы делим функцию на функцию.Это называется нахождением предела, когда знаменатель дроби стремится к нулю.

Примеры:
2x/x - всегда равно двум, даже когда x=0
1/x - бесконечность
sin(x)/x - при x=0 равно 1

Вот с последним случаем мы и нарвались на чудеса

В нашей программе встретился код, вычисляющий sin(x)/x при x -> 0

double step=PI/100;
for(int i=0; i<=100;i++){
   double angle = PI-i*step;
   double value=sin(angle)/angle;
   print(value);
}

Когда i==100 то angle=PI-100*PI/100 т.е. ноль.

Этот кусок кода успешно работал на 32 разрядном компьютере и выдавал на экран честную математическую единицу. Когда же его перенесли на 64 разрядный компьютер, то код взбесился и начал выдавать NaN (Not a number - Не число).

Фокус оказался в том, что на компьютере вычисления с вещественными числами выполняются приближенно (см. эппиграф). На 32 разрядном компьютере за счет округлений angle=PI-100*PI/100 получилось не точно ноль, а очень маленькое, но ненулевое число. sin(angle)/angle для такого маленького числа с учетом округлений равен единице, что и было напечатано. На 64 разрядной машине вычисления были произведены с большей точностью и angle получился строго равным нулю, а на ноль, все таки, делить нельзя.

Для программистов более точное объяснение от Александра Берсенева:

Разобрался, при 64 битах по умолчанию используются команды процессора SSE, а при 32 битах - FPU. Если явно указывать, то результат один и тот же и при -m64, и при -m32

#include <iostream> 

int main() { 
  double a = 0.5235987755982988; 
  double b = 3*a - 1.5707963267948965; 
  std::cout<<sizeof(b)<<' '<<b<<"\n"; 
}

$ g++ -m64 -mfpmath=387 1.cpp && ./a.out 
8 -1.11022e-16 
$ g++ -m32 -mfpmath=387 1.cpp && ./a.out 
8 -1.11022e-16 
$ g++ -m64 -mfpmath=sse 1.cpp && ./a.out 
8 0 
$ g++ -m32 -mfpmath=sse 1.cpp && ./a.out 
8 0
Tags: #include, математика, программы
Subscribe

  • Село Льва-Толстого

    Википедия может писать, что это "Село Льва Толстого" и даже "Село имени Льва Толстого", но официальная табличка на здании администрации не может…

  • Вокзал города Ирбита

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

  • Про ирбитскую ярмарку

    Две недели назад (как быстро летит время) в Ирбите прошла 18 традиционная ярмарка. Вообще говоря, с исторической ярмаркой это мероприятие ничего…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 1 comment