What is a short general method for testing properties against nan values ​​in F #?

I am doing property testing in F # with FsCheck. Therefore, I want to ensure that certain conditions are always satisfied regardless of the input arguments.

Consider that I define a trivial identity function for float values.

 let floatId (x : float) = x 

Then I define a test for this function, which, as I know, should always contain:

 let ``floatId returns input float`` x = floatId x = x 

This is a trivial test, I just make sure that calling my float identifier function returns the same as the input float.

Then I plug this function into FsCheck:

 Check.Quick ``floatId returns input float`` 

Unfortunately, this property test fails!

 Falsifiable, after 21 tests (0 shrinks) (StdGen (1872424299,296201373)): Original: nan 

Of course, looking back, it was quite obvious that this would happen, we know that nan <> nan .

Due to the structural comparison in F #, this can cause (slightly) more complex test cases related to collections.

If I create a similar function for floating lists:

 let listFloatId (lst : float list) = lst let ``listFloatId returns input float list`` lst = listFloatId lst = lst 
 Falsifiable, after 6 tests (3 shrinks) (StdGen (1874889363,296201373)): Original: [nan; 2.0; 2.25; 4.940656458e-324] Shrunk: [nan] 

Repeat the same problem!


Obviously, I can work around this problem by creating my own equality testing functions, which are great for float values, but it becomes more difficult to extend to collections like list , since I have to start using List.forall2 using my user interface equality function and usually specializes my code for each individual type of collection.

Is there a general way to solve this problem in F #?

+5
source share
1 answer

You can solve this problem using the LanguagePrimitives.GenericEqualityER function. This tests equality using the semantics of the equivalence relation. This function actually places a concrete example of comparing lists [nan] .

Test cases can be defined as follows:

 let ``ER : floatId returns input float`` x = LanguagePrimitives.GenericEqualityER (floatId x) x let ``ER : listFloatId returns input float list`` lst = LanguagePrimitives.GenericEqualityER (listFloatId lst) lst 

This time:

 Ok, passed 100 tests. Ok, passed 100 tests. 

(I ask and answer this question because the aforementioned property was raised on the FSharp Software Foundation Slack channel, and I thought it would be useful to have this solution on record. I can hardly mention this feature on the Internet outside the documentation in the LanguagePrimitives module).

+5
source

All Articles