2014-06-30 3 views
-3

Попытка напечатать более 15 десятичных цифр PI приведет к неправильной печати десятичных знаков после 15-го десятичного знака. Это несмотря на то, что 30 правильных десятичных значений назначены и, несмотря на использование long double для хранения значения. В следующем тестовом примере явно показана ошибка.Десятичная точность потеряна после 15-й цифры - Bad PI

Это неожиданно. Если бы произошла ошибка в цифрах, я бы не ожидал увидеть какую-либо ошибку до 25-й цифры после исчерпания значения IEEE-754. Какое правило играет здесь, что может объяснить, что я не могу распечатать те же 30 цифр, которые я только что назначил для sPI ниже. Это также влияет на способность печатать изображения M_PI, содержащиеся в math.h.

#include <stdio.h> 

int 
main (void) { 

    // static PI approximation (glibc man 1.17) 
    long double sPI = 3.14159265358979323846264338327; 
    char strPI[] = "3.14159265358979323846264338327"; 

    printf ("\n %s (strPI - string - correct)\n", strPI); 
    printf (" %.29Lf (sPI - long double - INCORRECT)\n\n", sPI); 

    return (0); 
} 

выход:

3.14159265358979323846264338327 (strPI - string - correct) 
3.14159265358979311599796346854 (sPI - long double - INCORRECT) 
       ^^^^^^^^^^^^^^ 

Предположительно, это десятичное ошибка будет применяться к любому десятичного числа с более чем 16 десятичных точностью. При печати в виде строки PI печатает отлично (очевидно), но при печати в виде двойной - десятичная точность ломается после 15-го десятичного знака. Что вызывает это?

Очень интересно, как предложил добавить L в конце плавающей точкой буквального помогло:

3.14159265358979323846264338327 (strPI - string - correct) 
3.14159265358979323851280895941 (sPI - long double - INCORRECT) 

Это при условии 3 дополнительных знаков после запятой точности. Для ясности, это работает на Linux 3.14.1 ядра, GCC 4.8.2 на старом AMD Phenom X4 9850. (AMD Turion ноутбук на базе Intel P4 & дает тот же результат)

Попытка с quadmath.h и __float128 типа присваивается sPI , результаты были такими же, как и для длинного двойника. Еще несколько цифр были доступны, но точность все равно сломалась на 19-й цифре: значения с плавающей точкой

3.14159265358979323846264338327 (strPI - string - correct) 
3.1415926535897932385128089594061862 (sPI - long double - INCORRECT) 
+5

Похоже, что 'long double' все еще 64-битная двойная точность. Не все компиляторы поддерживают 80-битную расширенную точность, и для тех, которые могут потребоваться компилятор. – Mysticial

+1

Вы не сказали, какой компилятор вы используете. Многие компиляционные платформы предлагают 80-битный 'long double' (GCC на Linux/x86, Clang на Linux/x86, ...), но даже больше определяют' long double' как тот же тип, что и 'double' (Visual Studio). Примечание: даже если 'long double' был 80-битным расширенным префиксом с плавающей запятой, вы не получили бы цифры из десяти десятичных цифр, что больше похоже на 19-21. –

+0

Вы понимаете, что назначение константы 'double' или' long double' для чего-либо с большей точностью не сделает вас счастливыми, верно? Используйте 'l' suffic для' long double' и анализируем константу из строки, если вы проходите мимо 'long double'. – tmyklebu

ответ

3

двойной точности представлены как 64 битное значение, с конечной точностью 15-16 десятичных цифр. Поэтому представляется, что long double реализована как 64-битная двойная точность в вашей системе.

Если вы не знакомы с этими проблемами, я настоятельно рекомендую вечную статью Дэвида Голдберга What Every Computer Scientist Should Know About Floating-Point Arithmetic.

+0

Спасибо, Дэвид. Это был документ, в который я не смог вернуться в свой поиск. Я был застрял в чтении добавлений к FXT, MPFR, GnuMP и пытался найти страницу, которая объяснила это, но, увы. Таким образом, мы получаем 15 цифр в реальном мире, если не используем многоточечный lib. –

+2

Некоторые системы имеют «длинный двойной», который может быть 80 бит, а другие - 128 бит. И другие, как и ваши, похоже, имеют 64-битный «длинный двойной». Но такие типы, как 'long double', всегда будут иметь конечную точность. Для большей точности вам нужны специализированные библиотеки. –

+0

Это также объясняет, почему единственный способ вывода цифр «PI» в алгоритмах, которые вычисляют его на миллионы мест, - это одна цифра за раз. Это также сбивало с толку вначале. У меня был небольшой фрагмент кода, который мог бы правильно напечатать 'PI' до любого десятичного числа, необходимого, но он сделал это по одному. –

4

Вы не храните значение «длинное двойное» в переменной, но вместо этого используется значение по умолчанию double. Компилятор считывает значение с плавающей запятой, сохраняет его как тип по умолчанию и только отбрасывает его до long double «afterwards». Вы можете увидеть это, когда сравниваете его значение с «обычно» назначенным double.

Чтобы указать компилятору на сохранение константы как long double, добавьте суффикс модификатора L или l в конце константы с плавающей запятой.

Пример:

#include <stdio.h> 

int main (void) 
{ 
    // static PI approximation (glibc man 1.17) 
    double sPI_d  = 3.14159265358979323846264338327; 
    long double sPI = 3.14159265358979323846264338327; 
    long double sPI_L= 3.14159265358979323846264338327L; 
    char strPI[]  ="3.14159265358979323846264338327"; 

    printf ("\n %s (strPI - string - correct)\n", strPI); 
    printf (" %.29f (sPI - double)\n", sPI_d); 
    printf (" %.29Lf (sPI - long double - INCORRECT)\n", sPI); 
    printf (" %.29Lf (sPI - long double - BETTER)\n", sPI_L); 

    return 0; 
} 

Выход:

3.14159265358979323846264338327 (strPI - string - correct) 
3.14159265358979311599796346854 (sPI - double) 
3.14159265358979311599796346854 (sPI - long double - INCORRECT) 
3.14159265358979323851280895941 (sPI - long double - BETTER) 

Смотрите также What is the precision of long double in C++? - вы не можете ожидать больше, чем около 18 значащих цифр для long double.

+0

Спасибо Johnware. Я также изучу 'quadmath.h' и' __float128', чтобы узнать, могу ли я получить другое место или два с 80-битной плавающей запятой. –

+0

'quadmath.h' кажется интересным! Bog standard 'math.h' определяет множество цифр, но кажется, что литье' (long double) M_PI' приходит «слишком поздно», так как у меня все еще есть его двойное значение. – usr2564301

Смежные вопросы