Точность с плавающей точкой в ​​сборке MIPS

Я написал два файла кода в сборке MIPS для выражения ниже:

R(n) = (от i до n) SUM { (i+2)/(i+1- 1/i) - i/(i+ 1/i) }

Один код вычисляет все выражение R(n) как суммирование и выдает результат.

Второй код сначала вычисляет первое слагаемое, т.е. (i + 2) / (i + 1- 1 / i) в цикле, а затем вычисляет второе слагаемое, т.е. i / (i + 1 / i) в другом цикле. Затем он просто вычитает две суммы.

Ниже приведены результаты для двух программ для разных значений n:

Программа 1:

 N        Result
-----------
10        5.07170725

100       7.41927338

1000      9.72636795

10000    12.02908134

100000   14.33149338

1000000  16.63462067

Программа 2:

 N       Result
---------
10       5.07170773

100      7.41923523

1000     9.72259521

10000   12.31250000

100000   8.61718750

1000000  6.50000000

Программа 1 дает более точные результаты (по сравнению с результатами Wolfram Alpha для R(n)). Почему программа 2 дает странные результаты при больших значениях n? Мой вопрос связан с точностью с плавающей запятой здесь.

Примечание: я использую числа одинарной точности.

1 ответ

Решение

Скажем, у вас есть un=an-bn и вы хотите сумму (un)

lim an -> 1, когда n -> бесконечность, поэтому сумма P членов стремится к P + cte_a, то же самое для bn, сумма стремится к P + cte_b

Когда вы различаете два (P + cte_a) - (P + cte_b), вы должны математически получить сумму (un).

Но с плавающей точкой это не то, что происходит, потому что (P + cte_a) округляется до ближайшего числа с плавающей точкой. И чем больше P, тем меньше float (P + cte_a) -float (P) будет близко к cte_a...

Чтобы убедить себя, попробуйте оценить эти операции:

10.0f+0.1f-10.0f
100.0f+0.1f-100.0f
...
1.0e7f+0.1f-1.0e7

lim un -> 1 / n, когда n -> бесконечность, поэтому программа 1 работает немного лучше...

Другие вопросы по тегам