Функция pow() работает некорректно при использовании больших чисел?

Пожалуйста, объясните, почему pow() не работает для больших чисел, таких как 2^63? (если вы напишете функцию pow с прототипом pow(long long int a, long long int b) вместо pow(double a, double b), она будет работать нормально.)

вот код:

#include <stdio.h>
#include <math.h>
int main()
{
    printf("%.0lf\n" ,pow((double)2,63));
}

выход:

9223372036854775800

верный ответ:

9223372036854775808

Благодарю.


person Fatemeh Karimi    schedule 23.03.2017    source источник
comment
Включите в свой вопрос минимально воспроизводимый пример.   -  person Paul R    schedule 23.03.2017
comment
Define работает некорректно, в идеале с РЕАЛЬНЫМ выводом   -  person fvu    schedule 23.03.2017
comment
Вы знаете, что означают разные типы?   -  person Eugene Sh.    schedule 23.03.2017
comment
Какой был тип возврата в вашем pow(long long int a, long long int b) примере?   -  person chux - Reinstate Monica    schedule 23.03.2017
comment
@chux это тоже длинное длинное целое   -  person Fatemeh Karimi    schedule 23.03.2017
comment
См. Википедию на двойной точности с плавающей запятой IEEE 754. Тип double обычно использует 12 бит для знака и экспоненты, оставляя 52 (фактически 53) бита для хранения мантиссы. В то время как 2 ^ 63 имеет только 1 двоичный бит, при многократном делении на 10 для создания двоичного представления вы постепенно используете больше битов, и вы можете начать сталкиваться с проблемами, требуя больше битов для точного представления оставшегося значения. Я не уверен, что это причина, но есть пределы точности всех этих вещей.   -  person Jonathan Leffler    schedule 24.03.2017


Ответы (2)


printf("%.0lf\n", x); не требуется, чтобы напечатать точное значение (double) x.

Совместимый компилятор C/стандартная библиотека C сгенерирует код, который напечатает около DBL_DECIMAL_DIG (обычно 17) с правильными значащими десятичными цифрами. Другие могут правильно печатать больше цифр. (Мой напечатан 9223372036854775808). См. <float.h>

// 45678901234567
9223372036854775800 // answer
9223372036854775808 // true answer:

Экстремальный пример

DBL_MAX может иметь точное значение

179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368

Тем не менее, многие результаты printf("%.0lf\n" ,DBL_MAX); будут содержать нули после определенного момента.

179769313486231570814527423731704356798070600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

OP позже добавил, что эталонная функция pow() была long long int pow(long long int a, long long int b). Ожидается, что pow_ll_version(2,63) приведет к 9223372036854775808, то есть 1 передал много LLONG_MAX - безусловно, неопределенное поведение. Предположим, что он обернут в -9223372036854775808 и напечатан с printf("%llu\n" , long_long_int_result); (больше UB из-за несоответствия спецификатора и типа). Это могло дать 9223372036854775808 OP.

printf() вывод целых чисел точен, а не значений с плавающей запятой.

person chux - Reinstate Monica    schedule 23.03.2017
comment
Компилятор ничего не напечатает. Это реализация стандартной библиотеки, то есть среды. - person too honest for this site; 23.03.2017

pow работает нормально. MSVC printf содержит ошибки и усекает вывод для плавающей запятой.

person R.. GitHub STOP HELPING ICE    schedule 23.03.2017
comment
Почему минус? 9223372036854775800 не является возможным значением, которое может быть представлено в double, поэтому, очевидно, это не то, что вернул pow. Единственный способ, которым он мог появиться, - это printf напечатать его неправильно, и единственная известная мне реализация с этой ошибкой - это MSVC. - person R.. GitHub STOP HELPING ICE; 23.03.2017
comment
@R.. Я не ставил тебе минус!! - person Fatemeh Karimi; 23.03.2017
comment
@FatemehKarimi Я тоже не минусовал, но вы могли бы быть более полезными, обновив используемый вами компилятор/библиотеку (независимо от того, MSVC это или нет; если да, то версию, которую вы используете), и как вы компилируете и т. д. - person P.P; 23.03.2017
comment
NMDV, но печать...75800 не глючит, (соответствует C IMO), так как слабовата. Только редкие реализации не прибегают к усечению в какой-то момент для больших значений. - person chux - Reinstate Monica; 23.03.2017
comment
@chux: Хотя это не запрещено (хотя в POSIX есть довольно строгие правила относительно того, какое значение должно быть напечатано; я не помню, делает ли это и простой C), это все равно составляет ошибку качества реализации в моей книге. Существует несколько хороших реализаций, способных печатать точное значение или правильно округленное значение с требуемой точностью. Печатать нули после DECIMAL_DIG места просто лениво и некачественно, а MSVC делает это чем-то вроде исключения. - person R.. GitHub STOP HELPING ICE; 23.03.2017