Предполагая, что n
и m
— положительные целые числа, а nm находится в пределах целого числа, будет ли (int)pow(n,m)
когда-либо давать неправильный ответ?
Я пробовал много n
вместо m=2
и до сих пор не получил ни одного неправильного ответа.
Предполагая, что n
и m
— положительные целые числа, а nm находится в пределах целого числа, будет ли (int)pow(n,m)
когда-либо давать неправильный ответ?
Я пробовал много n
вместо m=2
и до сих пор не получил ни одного неправильного ответа.
Стандарт C не предъявляет никаких требований к точности арифметических операций с плавающей запятой. Точность определяется реализацией, что означает, что реализации должны ее документировать. Однако реализации остаются со значительным «отсутствием»: (5.2.4.2.2, абзац 6, курсив добавлен.)
Точность операций с плавающей запятой (
+
,-
,*
,/
) и библиотечных функций в<math.h>
и<complex.h>
, которые возвращают результаты с плавающей запятой, определяется реализацией, как и точность преобразования между внутренними представлениями с плавающей запятой. и строковые представления, выполняемые библиотечными функциями в<stdio.h>
,<stdlib.h>
и<wchar.h>
. В реализации может быть указано, что точность неизвестна.
И действительно, gcc использует это преимущество, указывая, что точность неизвестна. Тем не менее, точность вычислений glibc довольно высока, хотя и не гарантируется.
Известно, что реализация MS libc иногда выдает ошибку 1ULP для функции pow
с целочисленными аргументами, что приводит к неправильному значению, если результат операции pow
просто усекается до int
. (Я не смог найти никаких спецификаций в документации Visual Studio о точности с плавающей запятой, но я считаю, что приведенный ниже список вопросов SO подтверждает мое утверждение.)
В архитектурах x86 большинство реализаций пытаются реализовать IEEE 754, поскольку собственное представление с плавающей запятой соответствует. Однако до редакции 2008 года IEEE-754 требовал только правильно округленные результаты от +
, -
, *
, /
и sqrt
. После пересмотра рекомендуется, чтобы ряд других функций возвращал правильно округленные результаты, но все эти рекомендации необязательны, и лишь немногие математические библиотеки реализуют их все.
Если вы действительно хотите использовать pow
для вычисления целых степеней целых чисел, рекомендуется (и просто) использовать lround(pow(n, m))
вместо (long)(pow(n, m))
, что округлит результат до ближайшего целого числа, а не оптимистично полагаясь на положительную ошибку. Это должно дать правильное целочисленное значение для результатов до 252 (с удвоением IEEE-754), если pow
находится в пределах 1ULP. Между 252 и 253 ошибка 1ULP будет равна 0,5, что иногда округляется до неправильного целого числа. За пределами 253 не все целые числа могут быть представлены как двойные числа.
SO на самом деле полон вопросов, связанных с этой конкретной проблемой. Видеть:
и, несомненно, многие другие.
Некоторые реализации в любом случае оценивают pow(x,y)
как exp(y*log(x))
, а другие используют квадратное умножение для целочисленных показателей.
glibc, например, имеет 'C ' реализации, а также реализации для конкретных платформ с таблицами поиска, полиномиальными аппроксимациями и т. д. Многие производные от Sun/BSD, по-видимому, обрабатывают целочисленные показатели как особый случай.
Что еще более важно, IEEE-754 не требует этого. Он также не определяет требуемую точность результата. glibc документирует максимальное количество ошибок ulp, хотя эта страница может быть не актуальной.
Таким образом, не рассчитывайте на точный целочисленный результат, даже если pow(n,m)
имеет точное представление с плавающей запятой.
pow
реализуется через log
и exp
, эти операции должны правильно округляться по правилам IEEE, см. en.wikipedia.org/wiki/. Вопрос в том, если эти операции округлить правильно, будет ли ответ когда-либо неверным? Конечно, вы не можете предполагать, что каждая реализация C соответствует правилам IEEE, но большинство современных будут.
- person Mark Ransom; 02.09.2015
pow(x, y)
эквивалентно exp(y*log(x))
, а не exp(x*log(y))
- person phuclv; 03.09.2015
double
IEEE-754), то ближайшее число является самим целым числом, что делает предположение OP правильным.
- person fuz; 08.09.2015
malloc()
когда-либо будет успешным, но почти все программы предполагают это.
- person fuz; 08.09.2015
На платформах, где int
имеет 64 бита, а double
также имеет 64 бита, это может не работать, поскольку double
(аргументы и результат pow()
) не имеют достаточной мантиссы для точного представления каждого 64-битного целого числа. На обычной платформе, где int
имеет 32 бита, а double
является 64-битным типом с плавающей запятой IEEE 754, ваше предположение верно.
pow
гарантированно всегда возвращающей calculated >= actual
? Промежуточное округление с плавающей запятой может привести к немного меньшему значению, чем полное целое число, а приведение (int)
округляет в меньшую сторону.
- person Jongware; 02.09.2015
exp
и ряд приближений. Будет ли реализация (любая, обычная и т. д.) проверять, являются ли входные данные целыми числами, а затем устанавливать ограничение на количество проверок сходимости? (Я сразу вижу здесь проблему... как узнать, содержит ли double
a whole number
?)
- person Jongware; 02.09.2015
+
, -
, *
, /
) и библиотечных функций в <math.h>
и <complex.h>
, которые возвращают результаты с плавающей запятой, определяется реализацией.
- person rici; 02.09.2015
__STDC_IEC_559__
(большинство так и делают), то pow
должно быть точным, но мне нужно проверить дальше.
- person fuz; 02.09.2015
__STDC_IEC_559__
гарантирует функции, перечисленные в Приложении F, включая соответствие версии IEEE-754 1985 года, а не версии 2008 года. В 1985 году для правильного округления требовалось только +, -, *, / и sqrt, и даже в 2008 году дополнительные функции были необязательными. Несмотря на все это, я часто использую числа с плавающей запятой. Но когда я конвертирую в целое число, я обязательно округляю, а не усекаю.
- person rici; 02.09.2015
Это зависит от размеров ваших целых чисел и удвоений.
С 32-битным целым числом и 64-битным двойником IEEE 754 он даст вам правильный ответ для всех возможных целых чисел.
Число с плавающей запятой IEEE 754 binary64 (которое является обычным представлением двойных чисел на большинстве современных машин) может точно представлять все целые числа [0,2^53].
exp(log(n)*m)
и не могут вернуть правильный результат для многих целых чисел.
- person phuclv; 02.09.2015
int
является 64-битным целым числом,double
может не соответствовать точному представлениюINT_MAX
. В данном случае(int)pow(INT_MAX, 1) != INT_MAX
. - person EOF   schedule 02.09.2015