Objective C Pow функция смешанной точности результат

Общий вопрос о функции pow в target-c.

Почему следующий код выплевывает ans=4.9999999, когда base = 125

NSDecimalNumber * base = [[NSDecimalNumber alloc ]initWithString:@"125"];
NSDecimalNumber * root = [[NSDecimalNumber alloc] initWithString:@"3"];
double ans=pow(125, 1.0/[root doubleValue]);

и ровно 3, когда база = 27

NSDecimalNumber * base = [[NSDecimalNumber alloc ]initWithString:@"27"];
NSDecimalNumber * root = [[NSDecimalNumber alloc] initWithString:@"3"];
double ans=pow(125, 1.0/[root doubleValue]);

1 ответ

Решение

Здесь есть несколько факторов. Самое главное, 1.0/3.0 не совсем одна треть, так что вы не вычисляете кубический корень base, Вместо этого вычисление, которое вы указали:

base**0.333333333333333314829616256247390992939472198486328125

или же

exp(0.333333333333333314829616256247390992939472198486328125 * log(base))

когда base является 125, точный результат действительного числа этого вычисления:

4.999999999999999553291243227753830961690873860134487744...

Два ближайших представимых двойника к этому значению 5.0 а также:

4.99999999999999911182158029987476766109466552734375

Последнее значение чуть ближе к математически точному результату, поэтому pow Функция вернула наилучший (или "правильно округленный") ответ.

Когда вы делаете то же самое вычисление с base равный 27, математически точный результат действительного числа:

2.99999999999999981704430129767885583952101310176125736...

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

Так что в этих случаях pow Функция дает вам наиболее точные возможные результаты для вычислений, которые вы ему предложили. Теперь, что сказал, нет никакой гарантии, что pow делает это для всех возможных входов (и вообще, вообще-то, нет). Вы не получите такие точные результаты для всех входных данных, и вы получите значительно менее точные результаты для многих входных данных на некоторых платформах. В общем, вы не должны зависеть от последнего бита округления pow функция или любая функция, которая явно не определена для получения правильно округленных результатов.

Короче говоря, полученные вами результаты являются наилучшими из возможных, а на некоторых других платформах вам не повезет.


Альтернативы:

  • Вы можете рассмотреть возможность использования cbrt функция, которая также не гарантирует правильного округления, но, по крайней мере, вычисляет корень куба, а не 0,3333333333333333148296...th степень.

  • Если вы априори знаете, что результатом должно быть целое число, вы можете округлить его до ближайшего целого числа через round или же rint функция.

  • Если вам действительно требуется точное округление последнего бита, рассмотрите возможность использования библиотеки CRLibm. Это приведет к некоторым затратам производительности, но если вам абсолютно необходимо иметь правильное округление, это единственный хороший вариант (но учтите, что он даст точно такие же результаты для этих конкретных примеров).

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