2011-02-09 4 views
4

Для некоторой универсальной работы я должен приблизить некоторые числа - например, Эйлера с серией. Поэтому я должен добавить очень маленькие числа, но у меня проблемы с точностью. Если число очень мало, это не влияет на результат.Как выполнить высокоточные вычисления в D?

real s; //sum of all previous terms 
ulong k; //factorial 

s += 1.0/ k; 

после каждого шага, к становится еще меньше, но после 10-го раунда в результате не changeing больше и застрял на 2,71828

ответ

3

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

auto data = real[N]; 
foreach(i, ref v; data) { 
    v = Fn(i); 
} 

while(data.length > 1) { 
    data.sort(); // IIRC .sort is deprecated but I forget what replaced it. 
    data[1] += data[0]; 
    data = data[1..$]; 
} 

return data[0]; 

+0

отличный .. я попробую! – NaN

9

фиксированной точности с плавающей типы точек, те, изначально поддерживается ваш центральный процессор с плавающей точкой (float, double, real) не являются оптимальными для любых вычислений, для которых требуется много цифр точности, например, пример, который вы указали.

Проблема состоит в том, что эти типы с плавающей точкой имеют конечное число цифр точности (фактически двоичные цифры), которые ограничивают длину числа, которое может быть представлено таким типом данных. Тип float имеет предел приблизительно 7 десятичных цифр (например, 3.141593); тип double ограничен 14 (например, 3.1415926535898); и тип real имеет аналогичный предел (немного больше, чем у double).

Добавление чрезвычайно малых чисел к значению с плавающей запятой приведет к тому, что эти цифры будут потеряны. Смотрите, что происходит, когда мы добавим следующие два значения с плавающей точкой вместе:

float a = 1.234567f, b = 0.00000000
float c = a + b; 

writefln("a = %f b = %f c = %f", a, b, c); 

Оба a и b являются допустимыми значениями с плавающей точкой и удерживать около 7 цифр точности поштучно в изоляции. Но при добавлении только самые верхнее 7 цифр сохраняются, так как она становится засунули обратно в поплавок:

1.23456700=> 1.234567|00=> 1.234567 
           ^^^^^^^^^^^ 
         sent to the bit bucket 

Так c заканчивается равный a, поскольку тонкие цифры точности от добавления a и b просыпаются от ,

Here's another explanation of the concept, вероятно, намного лучше, чем мой.


Ответ на эту проблему - арифметика произвольной точности. К сожалению, поддержка арифметики произвольной точности не в аппаратном обеспечении ЦП; следовательно, это не (обычно) на вашем языке программирования. Тем не менее, существует множество библиотек, которые поддерживают типы с плавающей точкой произвольной точности и математику, которую вы хотите выполнить на них. См. this question для некоторых предложений. Вы, вероятно, сегодня не найдете для этого D-специфических библиотек, но есть много библиотек C (GMP, MPFR и т. Д.), Которые должны быть достаточно легкими для использования изолированно, и тем более, если вы можете найти D для одного из них.

2

Как уже упоминалось, вам нужно использовать некоторую стороннюю многоаспектную арифметическую библиотеку с плавающей запятой (я думаю, что у Tango или Phobos есть только модуль для целочисленной арифметики произвольной длины).

dil - это проект D, в котором используется MPFR. Там вы должны найти привязки.

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