www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - What am I doing wrong here - canFind with iota is not working

reply "Kingsley" <kingsley.hendrickse gmail.com> writes:
float oneDegree = (PI / 180.0);
float first = -(oneDegree * 10.0);
float second = (oneDegree * 10.0);
float step = 0.000001;
float[] r = iota(first,second,step).array;

writeln(r);

float item = 0.174531;
writeln(r.canFind(item));


// returns false for canFind - even though that float is in the 
array ???
Feb 26 2015
next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Thursday, 26 February 2015 at 10:55:43 UTC, Kingsley wrote:
 float oneDegree = (PI / 180.0);
 float first = -(oneDegree * 10.0);
 float second = (oneDegree * 10.0);
 float step = 0.000001;
 float[] r = iota(first,second,step).array;

 writeln(r);

 float item = 0.174531;
 writeln(r.canFind(item));


 // returns false for canFind - even though that float is in the 
 array ???
Never check floating point numbers for equality, unless you've really thought about how the floating point arithmetic for this specific problem will go. Pretty much the only place it's useful to do is in unit tests for maths libraries.
Feb 26 2015
next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Thursday, 26 February 2015 at 11:00:05 UTC, John Colvin wrote:
 On Thursday, 26 February 2015 at 10:55:43 UTC, Kingsley wrote:
 float oneDegree = (PI / 180.0);
 float first = -(oneDegree * 10.0);
 float second = (oneDegree * 10.0);
 float step = 0.000001;
 float[] r = iota(first,second,step).array;

 writeln(r);

 float item = 0.174531;
 writeln(r.canFind(item));


 // returns false for canFind - even though that float is in 
 the array ???
Never check floating point numbers for equality, unless you've really thought about how the floating point arithmetic for this specific problem will go. Pretty much the only place it's useful to do is in unit tests for maths libraries.
This is the classic reference on the topic: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html but there are loads of other less rigorous explanations around the web. To cut a long story short: floating point maths is not the same as maths with real numbers. It's mostly roughly the same, sometimes catastrophically different and very, very rarely exactly the same.
Feb 26 2015
prev sibling parent reply "Laeeth Isharc" <Laeeth.nospam nospam-laeeth.com> writes:
Hi John.

Tks help with ldc - will look at that shortly.

So Kingsly needs to use a predicate for canFind that returns true 
if the two values being compared are close enough to being the 
same given floating point quirks ?

Ie I think people diagnosed the problem, but what is the 
solution...

On Thursday, 26 February 2015 at 11:00:05 UTC, John Colvin wrote:
 On Thursday, 26 February 2015 at 10:55:43 UTC, Kingsley wrote:
 float oneDegree = (PI / 180.0);
 float first = -(oneDegree * 10.0);
 float second = (oneDegree * 10.0);
 float step = 0.000001;
 float[] r = iota(first,second,step).array;

 writeln(r);

 float item = 0.174531;
 writeln(r.canFind(item));


 // returns false for canFind - even though that float is in 
 the array ???
Never check floating point numbers for equality, unless you've really thought about how the floating point arithmetic for this specific problem will go. Pretty much the only place it's useful to do is in unit tests for maths libraries.
Feb 26 2015
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Laeeth Isharc:

 Ie I think people diagnosed the problem, but what is the 
 solution...
A possible solution: void main() safe { import std.stdio, std.range, std.algorithm, std.math; immutable float oneDegree = (PI / 180.0f); immutable float first = -(oneDegree * 10.0f); immutable float second = (oneDegree * 10.0f); immutable float step = 0.000001f; immutable float[] r = iota(first, second, step).array; //r.writeln; immutable float item = 0.174531f; r.canFind!q{ feqrel(cast()a, cast()b) >= 21 }(item).writeln; } Bye, bearophile
Feb 26 2015
parent "bearophile" <bearophileHUGS lycos.com> writes:
     immutable float item = 0.174531f;
     r.canFind!q{ feqrel(cast()a, cast()b) >= 21 }(item).writeln;
 }
With a recent Phobos bug fix you can now write: r.canFind!q{ feqrel(a, b) >= 21 }(item).writeln; Bye, bearophile
Feb 27 2015
prev sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Thursday, 26 February 2015 at 12:39:20 UTC, Laeeth Isharc 
wrote:
 Hi John.

 Tks help with ldc - will look at that shortly.

 So Kingsly needs to use a predicate for canFind that returns 
 true if the two values being compared are close enough to being 
 the same given floating point quirks ?

 Ie I think people diagnosed the problem, but what is the 
 solution...
My preference would be to find a way to construct the program such that it isn't needed. Failing that, std.math has feqrl and approxEqual for this task.
Feb 26 2015
parent "Laeeth Isharc" <nospamlaeeth nospam.laeeth.com> writes:
Thanks.

Rough version for Wiki here:
http://wiki.dlang.org/Floating_Point_Gotchas

It could be tidier, but I am not able to do so at moment.  Feel 
free to change.

On Thursday, 26 February 2015 at 14:04:17 UTC, John Colvin wrote:
 On Thursday, 26 February 2015 at 12:39:20 UTC, Laeeth Isharc 
 wrote:
 Hi John.

 Tks help with ldc - will look at that shortly.

 So Kingsly needs to use a predicate for canFind that returns 
 true if the two values being compared are close enough to 
 being the same given floating point quirks ?

 Ie I think people diagnosed the problem, but what is the 
 solution...
My preference would be to find a way to construct the program such that it isn't needed. Failing that, std.math has feqrl and approxEqual for this task.
Feb 26 2015
prev sibling parent reply "Baz" <bb.temp gmx.com> writes:
On Thursday, 26 February 2015 at 10:55:43 UTC, Kingsley wrote:
 float oneDegree = (PI / 180.0);
 float first = -(oneDegree * 10.0);
 float second = (oneDegree * 10.0);
 float step = 0.000001;
 float[] r = iota(first,second,step).array;

 writeln(r);

 float item = 0.174531;
 writeln(r.canFind(item));


 // returns false for canFind - even though that float is in the 
 array ???
also mark your float litteral with the f postfix. By default FP litterals are double...IIRC float oneDegree = (PI / 180.0f); float first = -(oneDegree * 10.0f); float second = (oneDegree * 10.0f); float step = 0.000001f; float[] r = iota(first,second,step).array; writeln(r); float item = 0.174531f; writeln(r.canFind(item));
Feb 26 2015
parent reply "Kingsley" <kingsley.hendrickse gmail.com> writes:
On Thursday, 26 February 2015 at 11:04:58 UTC, Baz wrote:
 On Thursday, 26 February 2015 at 10:55:43 UTC, Kingsley wrote:
 float oneDegree = (PI / 180.0);
 float first = -(oneDegree * 10.0);
 float second = (oneDegree * 10.0);
 float step = 0.000001;
 float[] r = iota(first,second,step).array;

 writeln(r);

 float item = 0.174531;
 writeln(r.canFind(item));


 // returns false for canFind - even though that float is in 
 the array ???
also mark your float litteral with the f postfix. By default FP litterals are double...IIRC float oneDegree = (PI / 180.0f); float first = -(oneDegree * 10.0f); float second = (oneDegree * 10.0f); float step = 0.000001f; float[] r = iota(first,second,step).array; writeln(r); float item = 0.174531f; writeln(r.canFind(item));
Adding the f still produces a false result. Also I tried changing all to double but still not working - I always get false back. Interestingly if I harcode the output of the iota into an array - then things start to work as I expect.
Feb 26 2015
next sibling parent reply "Kingsley" <kingsley.hendrickse gmail.com> writes:
On Thursday, 26 February 2015 at 11:12:42 UTC, Kingsley wrote:
 On Thursday, 26 February 2015 at 11:04:58 UTC, Baz wrote:
 On Thursday, 26 February 2015 at 10:55:43 UTC, Kingsley wrote:
 float oneDegree = (PI / 180.0);
 float first = -(oneDegree * 10.0);
 float second = (oneDegree * 10.0);
 float step = 0.000001;
 float[] r = iota(first,second,step).array;

 writeln(r);

 float item = 0.174531;
 writeln(r.canFind(item));


 // returns false for canFind - even though that float is in 
 the array ???
also mark your float litteral with the f postfix. By default FP litterals are double...IIRC float oneDegree = (PI / 180.0f); float first = -(oneDegree * 10.0f); float second = (oneDegree * 10.0f); float step = 0.000001f;
Hardcoding the double[] does work as expected - so why doesn't it work with the iota generated array? double oneDegree = (PI / 180.0); double first = -(oneDegree * 10.0); double second = (oneDegree * 10.0); double step = 0.000001; double[] r = iota(first,second,step).array; writeln(r); double[] hardCoded = [ 0.174521, 0.174522, 0.174523, 0.174524, 0.174525, 0.174526, 0.174527, 0.174528, 0.174529, 0.17453, 0.174531, 0.174532]; double item = 0.174531; writeln(r.canFind(item)); // false - I expect true!!!! writeln(hardCoded.canFind(item)); // true - as I expect
 float[] r = iota(first,second,step).array;

 writeln(r);

 float item = 0.174531f;
 writeln(r.canFind(item));
Adding the f still produces a false result. Also I tried changing all to double but still not working - I always get false back. Interestingly if I harcode the output of the iota into an array - then things start to work as I expect.
Feb 26 2015
parent "Fool" <fool dlang.org> writes:
On Thursday, 26 February 2015 at 11:17:12 UTC, Kingsley wrote:
 Hardcoding the double[] does work as expected - so why doesn't 
 it work with the iota generated array?
'double' represents a floating-point type with base 2. This implies that decimal numbers like 0.1 (= 1/10 = 1/(2*5)) cannot be exactly represented using double. The double literal 0.1 refers to a number close to but not equal to the real number 0.1. So when you use a literal step of 0.1 you actually use step 1.00000000000000005551115123126E-1. The small error adds up in successive additions and, due to a finite number of digits, every addition can introduce further error.
Feb 26 2015
prev sibling parent "Baz" <bb.temp gmx.com> writes:
On Thursday, 26 February 2015 at 11:12:42 UTC, Kingsley wrote:
 On Thursday, 26 February 2015 at 11:04:58 UTC, Baz wrote:
 On Thursday, 26 February 2015 at 10:55:43 UTC, Kingsley wrote:
 float oneDegree = (PI / 180.0);
 float first = -(oneDegree * 10.0);
 float second = (oneDegree * 10.0);
 float step = 0.000001;
 float[] r = iota(first,second,step).array;

 writeln(r);

 float item = 0.174531;
 writeln(r.canFind(item));


 // returns false for canFind - even though that float is in 
 the array ???
also mark your float litteral with the f postfix. By default FP litterals are double...IIRC float oneDegree = (PI / 180.0f); float first = -(oneDegree * 10.0f); float second = (oneDegree * 10.0f); float step = 0.000001f; float[] r = iota(first,second,step).array; writeln(r); float item = 0.174531f; writeln(r.canFind(item));
Adding the f still produces a false result. Also I tried changing all to double but still not working - I always get false back. Interestingly if I harcode the output of the iota into an array - then things start to work as I expect.
Sorry, in the past i've found that similar questions about FP can be solved by following this way. There must be something that is not 'float' in the processing...
Feb 26 2015