문제

I have an array of "x" values (a grid for a PDE solver) that when I pass to a function that fills another array based on these x-values, a particular expression involving one x-value does not evaluate properly. The x-values range is -1:1 at an increment of 0.0125, and at x = -0.5, and at x = 0.5, I need to handle these cases differently than the other values. However the block below fails to evaluate to TRUE for the point x = 0.5 (it's ok for x = -0.5). Here's a stripped down snippet of the problem block, with details to follow:

int N = 160;
double delta_x = 0.0125;
const double lims = 0.5 * delta_x;

for(int i = 0; i <= N; i++)
{                                
  if((x[i] < -0.5) || (x[i] > 0.5)) sol[i] = 0;
  else if( (abs(x[i] + 0.5) < lims) || (abs(x[i] - 0.5) < lims) )  sol[i] = 0.5;
  else sol[i] = 1;

  cout << setprecision(30) << "lims: " << lims << ", abs(x[i] - 0.5): " << abs(x[i] - 0.5) << endl;
  cout << "sol[" << i << "]: " << sol[i] << endl;                                                                                         
}

Here is the output for x = 0.5:

lims: 0.00625000000000000034694469519536, abs(x[i] - 0.5): 1.11022302462515654042363166809e-16
sol[120]: 0

So, it's looks like the expression in the if-statement should return TRUE when x = 0.5, even though it's not precisely 0.5 of course, since it is within the "lims" of the range. Any thoughts??

도움이 되었습니까?

해결책

Answer to the original question

The expression in the if statement did evaluate true. If it did not, then you would not have seen the output that you included in the question.

FWIW, a simpler test would be for

abs(abs(x[i]) - 0.5) < lims

Answer to the latest version of the question

if((x[i] < -0.5) || (x[i] > 0.5)) sol[i] = 0;
else if( (abs(x[i] + 0.5) < lims) || (abs(x[i] - 0.5) < lims) )  sol[i] = 0.5;
else sol[i] = 1;

You state that x[i] is close to 0.5, but does not set sol[i] to 0.5 and in fact sets it to 0. In which case the only sane conclusion is that x[i] > 0.5 and the first condition is is met:

if((x[i] < -0.5) || (x[i] > 0.5)) sol[i] = 0;

So you need to change the order of your tests:

if( (abs(x[i] + 0.5) < lims) || (abs(x[i] - 0.5) < lims) )  sol[i] = 0.5;
else if((x[i] < -0.5) || (x[i] > 0.5)) sol[i] = 0;
else sol[i] = 1;

And I would write it like this:

if (abs(abs(x[i]) - 0.5) < lims)
    sol[i] = 0.5;
else if ((x[i] < -0.5) || (x[i] > 0.5)) 
    sol[i] = 0;
else 
    sol[i] = 1;

Please in future be sure to include the right code in the question that you ask.

다른 팁

Well, now after you corrected your code, the problem is obvious. For x[i] = 0.5 the control is intercepted by the very first condition

if((x[i] < -0.5) || (x[i] > 0.5)) sol[i] = 0;

It doesn't even get to your abs evaluations.

Floating-point number are not always precise. While it is true that 0.5 can be represented precisely, it only applies to situations when you assign 0.5 directly or use some simple and particularly stable evaluations to arrive at that value (like in case of 1.0/2). In more complex cases you might end up with imprecise 0.5 value (like in case of 0.1 * 5). And that is what's causing it to get intercepted by that first if. Your positive 0.5 is imprecise and just happens to be greater than the precise 0.5.

One way to fix it might be to put your approximate lim-based comparisons first and make "precise" comparisons follow

if( (abs(x[i] + 0.5) < lims) || (abs(x[i] - 0.5) < lims) )  sol[i] = 0.5; 
else if((x[i] < -0.5) || (x[i] > 0.5)) sol[i] = 0;
else sol[i] = 1;
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top