Будет ли (int)pow(n,m) неправильным для некоторых натуральных чисел n,m?

Предполагая, что n и m — положительные целые числа, а nm находится в пределах целого числа, будет ли (int)pow(n,m) когда-либо давать неправильный ответ?

Я пробовал много n вместо m=2 и до сих пор не получил ни одного неправильного ответа.


person user1537366    schedule 02.09.2015    source источник
comment
если m отрицательное, вы можете легко получить неправильный ответ (например, 2^-2)   -  person DavidW    schedule 02.09.2015
comment
@DavidW Извините, я имел в виду положительные целые числа.   -  person user1537366    schedule 02.09.2015
comment
@user1537366 user1537366 Упомяните об этом.   -  person ameyCU    schedule 02.09.2015
comment
Если int является 64-битным целым числом, double может не соответствовать точному представлению INT_MAX. В данном случае (int)pow(INT_MAX, 1) != INT_MAX.   -  person EOF    schedule 02.09.2015
comment
Да, это будет. Например. при n = 2 и m › количество значащих цифр в мантиссе.   -  person The Paramagnetic Croissant    schedule 02.09.2015


Ответы (4)


Стандарт 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 на самом деле полон вопросов, связанных с этой конкретной проблемой. Видеть:

и, несомненно, многие другие.

person rici    schedule 02.09.2015
comment
Сэр, можете ли вы увидеть этот вопрос stackoverflow.com/questions/42164550/ , речь идет о pow, но на данный момент нет хорошего ответа. - person Suraj Jain; 11.02.2017

Некоторые реализации в любом случае оценивают pow(x,y) как exp(y*log(x)), а другие используют квадратное умножение для целочисленных показателей.

glibc, например, имеет 'C ' реализации, а также реализации для конкретных платформ с таблицами поиска, полиномиальными аппроксимациями и т. д. Многие производные от Sun/BSD, по-видимому, обрабатывают целочисленные показатели как особый случай.

Что еще более важно, IEEE-754 не требует этого. Он также не определяет требуемую точность результата. glibc документирует максимальное количество ошибок ulp, хотя эта страница может быть не актуальной.

Таким образом, не рассчитывайте на точный целочисленный результат, даже если pow(n,m) имеет точное представление с плавающей запятой.

person Brett Hale    schedule 02.09.2015
comment
Я думаю, что ответ сложнее, чем этот. Даже если pow реализуется через log и exp, эти операции должны правильно округляться по правилам IEEE, см. en.wikipedia.org/wiki/. Вопрос в том, если эти операции округлить правильно, будет ли ответ когда-либо неверным? Конечно, вы не можете предполагать, что каждая реализация C соответствует правилам IEEE, но большинство современных будут. - person Mark Ransom; 02.09.2015
comment
@MarkRansom: как указывает эта ссылка, эти операции рекомендуются, а соответствие не является обязательным. В любом случае, большинство современных реализаций C соответствуют IEEE-754-1985, согласно Приложению F текущего стандарта C; нет оснований предполагать, что они будут соответствовать дополнительным функциям IEEE-754-2008. - person rici; 02.09.2015
comment
pow(x, y) эквивалентно exp(y*log(x)), а не exp(x*log(y)) - person phuclv; 03.09.2015
comment
Можете ли вы назвать конкретную реализацию, которая широко распространена и делает это? - person fuz; 08.09.2015
comment
@FUZxxl - это прямо там, во второй ссылке, которую я предоставил. - person Brett Hale; 08.09.2015
comment
@BrettHale Но в документации утверждается: «Учитывая два двойных машинных номера IEEE y, x, он вычисляет правильно округленное (до ближайшего) значение X ^ y». Таким образом, он не дает неправильных результатов для целых оснований и показателей. - person fuz; 08.09.2015
comment
@FUZxxl - я не проверял результаты. Но я так понимаю, что «правильное округление до ближайшего» означает RN{x}, x в пределах 1/2ulp от точного результата здесь. - person Brett Hale; 08.09.2015
comment
@BrettHale Но если целое число можно представить точно как число с плавающей запятой (все 32-битные целые числа можно представить как числа double IEEE-754), то ближайшее число является самим целым числом, что делает предположение OP правильным. - person fuz; 08.09.2015
comment
Опять же, вы еще не показали мне ни одного случая в общей реализации, которая дает неверный результат в рамках ограничений, указанных OP. - person fuz; 08.09.2015
comment
@FUZxxl - Бремя доказательства на самом деле не лежит на мне. Я придерживаюсь требований IEEE-754. Ваш ответ утверждает точные результаты без ссылки на какой-либо стандарт или реализацию, не говоря уже о всех реализациях. В верхнем ответе уже указывалось, что это не относится к реализации MS - если это достаточно широко распространено для вас. - person Brett Hale; 08.09.2015
comment
@BrettHale Ну, до сих пор каждая отдельная реализация, процитированная кем-либо из нас, имеет свойство, о котором спрашивал OP. Так почему же вы до сих пор отказываетесь верить, что это действительно так? Ясно, что ни один из нас не смог найти общую реализацию libc, которая не проявляла бы свойства OP. Конечно, стандарт не гарантирует этого, но он также не гарантирует, что malloc() когда-либо будет успешным, но почти все программы предполагают это. - person fuz; 08.09.2015

На платформах, где int имеет 64 бита, а double также имеет 64 бита, это может не работать, поскольку double (аргументы и результат pow()) не имеют достаточной мантиссы для точного представления каждого 64-битного целого числа. На обычной платформе, где int имеет 32 бита, а double является 64-битным типом с плавающей запятой IEEE 754, ваше предположение верно.

person fuz    schedule 02.09.2015
comment
На обычной платформе, где int имеет 32 бита, а double — это 64-битный тип с плавающей запятой IEEE 754, ваше предположение верно. – нет, не совсем. - person The Paramagnetic Croissant; 02.09.2015
comment
Является ли (наиболее распространенная и т. д.) реализация вычисления pow гарантированно всегда возвращающей calculated >= actual? Промежуточное округление с плавающей запятой может привести к немного меньшему значению, чем полное целое число, а приведение (int) округляет в меньшую сторону. - person Jongware; 02.09.2015
comment
@ThePara MagneticCroissant Как так? Скажите, пожалуйста, где его нет. - person fuz; 02.09.2015
comment
@FUZxxl Извините, я имел в виду другую ситуацию. - person The Paramagnetic Croissant; 02.09.2015
comment
@Jongware Стандарт гласит: «Функции pow вычисляют x в степени y». Я прочитал это как «результат должен быть номером с плавающим указателем, наиболее близким к фактическому результату». - person fuz; 02.09.2015
comment
@FUZxxl: ну конечно; по крайней мере, это общая идея. Однако мне кажется, что текущие реализации используют exp и ряд приближений. Будет ли реализация (любая, обычная и т. д.) проверять, являются ли входные данные целыми числами, а затем устанавливать ограничение на количество проверок сходимости? (Я сразу вижу здесь проблему... как узнать, содержит ли double a whole number?) - person Jongware; 02.09.2015
comment
@Jongware Принятый ответ на вопрос, который вы связали, показывает реализацию, которая специально проверяет целочисленную базу и показатель степени и является точной. Это из библиотеки Apple (BSD). - person fuz; 02.09.2015
comment
@FUZxxl: вы слишком много читаете в этом предложении из стандарта. См. 5.2.4.2.2/6 Точность операций с плавающей запятой (+, -, *, /) и библиотечных функций в <math.h> и <complex.h>, которые возвращают результаты с плавающей запятой, определяется реализацией. - person rici; 02.09.2015
comment
@rici Ну ладно, тогда не используй математику с плавающей запятой. Однако я считаю, что если ваша реализация определяет __STDC_IEC_559__ (большинство так и делают), то pow должно быть точным, но мне нужно проверить дальше. - person fuz; 02.09.2015
comment
@FUZxxl: __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].

person Art    schedule 02.09.2015
comment
@ThePara MagneticCroissant Покажите мне один контрпример положительных n и m, где это неверно. Я не могу придумать ни одного, но я открыт для того, чтобы ошибаться. - person Art; 02.09.2015
comment
Извините, я имел в виду другую ситуацию. Я все еще верю, что есть примеры, которые я ищу. - person The Paramagnetic Croissant; 02.09.2015
comment
это зависит от реализации. Некоторые плохие просто вычисляют pow(n, m) на exp(log(n)*m) и не могут вернуть правильный результат для многих целых чисел. - person phuclv; 02.09.2015
comment
несколько примеров: stackoverflow.com/q/7937286/995714 stackoverflow.com/q/17122496/995714 - person phuclv; 02.09.2015