Что вижу - о том пою (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

  • Леда и лебедь

    Леда и лебедь от наших рекламщиков, надеюсь, что они знают о чём этот сюжет. Кстати, оказалось, что сфотографировать картинку с жк-панели очень…

  • На чешуе

    На чешуе жестяной рыбы Искал я зовы новых губ ... (В. Маяковский) Несколько лет назад это стихотворение висело в одном из переходов московского…

  • Машины нашего городка

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

  • 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