PR# 19491 Postcondition of `twin' violated with NaN

Problem Report Summary
Submitter: gobobe
Category: EiffelBase
Priority: Medium
Date: 2018/11/01
Class: Bug
Severity: Serious
Number: 19491
Release: 18.07.10.1981
Confidential: No
Status: Analyzed
Responsible:
Environment: win
Synopsis: Postcondition of `twin' violated with NaN

Description
The following code:

		local
			a, b: ANY
		do
			a := {REAL_64}.nan
			b := a

fails with this exception trace:

******************************** Thread exception *****************************
In thread           Root thread            0x0 (thread id)
*******************************************************************************
-------------------------------------------------------------------------------
Class / Object      Routine                Nature of exception           Effect
-------------------------------------------------------------------------------
REAL_64             twin @2                is_equal:
<00000241842B05F8>  (From ANY)             Postcondition violated.       Fail
-------------------------------------------------------------------------------
REAL_64             twin @2
<00000241842B05F8>  (From ANY)             Routine failure.              Fail
-------------------------------------------------------------------------------
GEPP                execute @2
<00000241842B05A8>                         Routine failure.              Fail
-------------------------------------------------------------------------------
GEPP                root's creation
<00000241842B05A8>                         Routine failure.              Exit
-------------------------------------------------------------------------------

--
Eric Bezault
To Reproduce

										
Problem Report Interactions
From:alexk_es    Date:2018/11/09    Status: Analyzed    Download   
The change that will follow solution C or D is planned for EiffelStudio 19.05 to prevent last minute breaking changes in 18.11. This will change the default of the option "total order on reals" for newly created projects. Old projects could still rely on the current behavior. However, the compiler will report a warning when "total order on reals" is set to False.

The current workaround is to set the option "total order on reals" to True for all projects explicitly.

From:gobobe    Date:2018/11/09    Status: Analyzed    Download   
In my opinion, variants A and B are a poor man's solution. The problem is in classes REAL_32 and REAL_64. So the fix should be applied in these classes, not in all possible clients of these classes. NaN is breaking Eiffel assertions even in these REAL classes. The invariant "recursive_equality" inherited from ANY is violated. Because of NaN we don't have the total order relation that is promised by the inheritance from COMPARABLE. From example:

    print ({REAL_64}.nan >= 3.0)
    print ({REAL_64}.nan.to_reference >= (3.0).to_reference)

the first one (everything built-in) returns False, the second one (using code inherited from COMPARABLE) returns True.

So I would favor variants C or D. But not only equality should be addressed but also comparison operators. And we should probably add:

    ieee_is_less alias "#<" (other: like Current): BOOLEAN

or something like that (and similar routines for the other comparison operators and equality) for the users who want to get the IEEE semantics.
Of cour
....
Output truncated, Click download to get the full message

From:alexk_es    Date:2018/11/09    Status: Analyzed    Download   
Just to clarify, what are the suggestions


Variant A:

1) twin (and similar ones: clone, copy, etc.) should change
      Result ~ Current
into
      Current = Current implies Result ~ Current

2) all containers should change postconditions like
      item (...) = value
into
      value = value implies item (...) = value


Variant B (similar to KL_TYPE.same_objects of Gobo):

1) twin (and similar ones: clone, copy, etc.) should change
      Result ~ Current
into
      safe_equal (Result, Current)
where
      safe_equal will do internally the checks of variant A (or use the code similar to KL_TYPE.same_objects)

2) all containers should change postconditions like
      item (...) = value
into
      safe_equal (item (...), value)


Variant C:

The compiler always treats NaN values as equal.


Variant D:

The compiler treats bit-wise identical NaN values as equal.

From:gobobe    Date:2018/11/02    Status: Analyzed    Download   
A final remark: I made sure that the postconditions in the container classes of Gobo (such as "last = v" in routines like `put_last (v: G)') have been adapted to deal with this NaN issue.
Let's not put our heads in the sand and claim that it's because of IEEE and that there is an ECF option. If the assertions are broken, they are broken and they need to be fixed.

From:gobobe    Date:2018/11/02    Status: Analyzed    Download   
I should add that this case with the assertion violation in `twin' is particularly problematic since, because of the call to `twin' which happens behind the scene, one cannot have an assignment. 

Interestingly, only the second assignment triggers the postcondition violation. Isn't `twin' called when assigning an expanded to a reference?

From:gobobe    Date:2018/11/02    Status: Analyzed    Download   
I know all that. This is not my problem. This is yours. I got a postcondition violation in {REAL_64}.twin. This neither my class nor my routine. Either {REAL_64} should not allow NaN, and if it does, then EiffelBase's assertions should take that special case into account.

From:alexk_es    Date:2018/11/02    Status: Analyzed    Download   
According to IEEE standard, NaN is not equal to NaN, thus the assertion violation. The option "Total order on REALs" can be turned on to disable this behavior and to consider NaN as equal to NaN:

   https://www.eiffel.org/doc/eiffelstudio/Advanced_Options

If the project uses a precompile, the precompile should be built with the same option. The project has to be rebuilt from scratch to take the new option value into account.

Feel free to close this problem report if you consider using the option as a satisfactory solution.