2012-06-26 3 views
3

У меня есть кодКак предотвратить переполнение в sqrt?

unsigned long long f(unsigned long long x, unsigned long long y){ 
    return sqrt(((5*x*x + 1) * (5*y*y +1) - 1)/4);   
} 

но если х или у слишком велика, вещь переполняется, даже если выход должен быть небольшим. Есть ли способ обойти это?

+0

Насколько точны ответы, которые вам нужны? – templatetypedef

+0

Переименуйте свою функцию ... это ужасно неочевидно, что она должна делать. И что сказал templatetypedef. – Brendan

+4

@Brendan: Вам пришло в голову, что это может быть частью некоторого кода приличия, и что использование реального имени для 'f' может нарушить NDA OP? Например, «f» в порядке. –

ответ

8

Сплит аргумент квадратного корня алгебраически, может быть ?:

return sqrt((3*x*x+1)/2) * sqrt((6*y*y-5)/2); 

Или разделить его дальше в зависимости от потребностей.

Если x достаточно велик, вы можете игнорировать +1 и сделать первый термин:

sqrt((3*x*x)/2) = fabs(x) * sqrt(3.0/2.0) 

и аналогично с y на второй срок, что делает его

sqrt((6*y*y)/2) = fabs(y) * sqrt(3.0); 

EDIT: После того, как OP отредактирован его вопрос будет следующим:

return sqrt(((3*x*x+1)*(6*y*y-5)-1)/4); 

На самом деле вы можете разделить вещи. Вам просто нужно быть немного осторожнее. Суть в том, что если x действительно большой, то +1 можно игнорировать. Если y действительно большой, то -5 может быть проигнорирован. Если оба значения (3*x*x+1) и (6*y*y-5) являются положительными и либо действительно большими, то -1 можно игнорировать. Вы можете использовать эти советы и некоторую дополнительную окружную логику, чтобы немного покончить с этим. Например:

if(fabs(x) > BIGNUMBER && fabs(y) > BIGNUMBER) 
{ 
    return fabs(x) * fabs(y) * sqrt(18.0/4.0); 
} 
if(fabs(x) > BIGNUMBER && fabs(y) > 1.0) // x big and y term positive 
{ 
    return fabs(x) * sqrt(6*y*y-5) * sqrt(3.0/2.0); 
} 
if(fabs(y) > BIGNUMBER) // x term positive and y big 
{ 
    return sqrt(3*x*x+1) * fabs(y) * sqrt(6.0/2.0); 
} 
return sqrt(((3*x*x+1)*(6*y*y-5)-1)/4); 

Вы можете оптимизировать это, но это только должно показать суть.

+0

Примечание: из-за целочисленного деления 'sqrt (3/2)' == 'sqrt (1)' == '1'. –

+0

Я технически имею -1 в моем уравнении. отредактировал – MyNameIsKhan

+0

@ Robᵩ Хорошо, я переключился на более символическую нотацию, но я сделаю этот C/C++ правильным. –

0

Я считаю, что при больших х или у символом -1 можно пренебречь (x * x) * (y * y) ... и поскольку ваша функция возвращается долго, точность с плавающей запятой не будет иметь значения ,

Вы можете проверить, являются ли x или y большими или нет, и, соответственно, вы можете игнорировать -1 и делать так, как говорят Крис или Роб.