Страницы

Поиск по вопросам

воскресенье, 8 марта 2020 г.

Почему при дробных значениях алгоритм не работает?

#delphi #алгоритм #pascal #delphi_7


Задача следующая:


  Ввести целое число a и вывести сумму квадратов всех чисел от 1 до a с шагом 0.1.
(1^2+1.1^2+1.2^2+...+a^2)


Алгоритм решения такой:

result := 0
while (a >= 1) do begin
    result := result + a * a;
    a := a - 0.1;
end;


В конце работы программы результат должен быть:

При a = 1.1 result = 2.21
При a = 3 result = 91.7


Переменная a, в любом случае, при завершении программы должна быть равна 0.9.

Проблема в следующем. При a = 1.1 все верно, но начиная с 1.2 и далее программа выдает
в результате на 1 меньше, чем нужно: a = 3; result = 90.7, при этом, в конце выполнения
программы, a = 0.9(9).

Если модифицировать алгоритм: переменную a после ввода умножать на 10, а затем в
цикле в квадрат возводить не ее, а выражение a / 10, то все начинает работать правильно
(второй строкой в цикле здесь будет: a := a - 1).

В чем может быть проблема?
    


Ответы

Ответ 1



Проблема в том, что числа представлены в двоичном виде. Десятичное значение 0,1 не может быть представлено в двоичном виде конечной дробью, оно равно 0,0001100110011...2 или 0,0(0011)2 в стандартной записи рациональных дробей. У чисел с плавающей точкой с двойной точностью хранится 53 двоичных знака после запятой, бесконечная часть дроби отбрасывается. В результате вместо точного значения 0,1 мы имеем число 0,1000000000000000055511151231257827021181583404541015625. Если вы выведете его на печать с не очень большой точностью, вы получите 0,1, но это только результат округления. При округлении до 17-го знака получается 0,100000000000000001, и последний знак участвует в вычислениях. Поэтому, два раза вычитая из 1,2 значение 0,1, вы получаете число меньшее 1. Простого решения подобных проблем на все случаи жизни не существует. Простейшим в вашем случае будет эмуляция чисел с фиксированной запятой, например так, как вы и сделали. Можно упростить вычисления, посчитав суммы квадратов целых чисел, и разделив результат на 100: a2 + (a + 0.1)2 + (a + 0.2)2 + ... = (10a)2/100 + (10a + 1)2/100 + (10a + 2)2/100 + ... Можно использовать библиотеки, которые обеспечивают работу с рациональными числами, с числами в десятичной системе счисления (как тип decimal в C#), или с числами большой точности.

Комментариев нет:

Отправить комментарий