> On 9 Apr 2020, at 23:59, Gilles Sadowski <[email protected]> wrote:
>
> Le jeu. 9 avr. 2020 à 23:20, Alex Herbert <[email protected]
> <mailto:[email protected]>> a écrit :
>>
>>
>>
>>> On 9 Apr 2020, at 21:36, Gilles Sadowski <[email protected]> wrote:
>>>
>>> Le jeu. 9 avr. 2020 à 22:20, Alex Herbert <[email protected]> a
>>> écrit :
>>>>
>>>>
>>>>
>>>>> On 9 Apr 2020, at 16:32, Gilles Sadowski <[email protected]> wrote:
>>>>>
>>>>>
>>>>>
>>>>>> Given this I am thinking that using ZERO when possible is a better
>>>>>> option and avoid 0 / -1.
>>>>>
>>>>> Hmm, then I'm both +0 and -0 (which is the same, right?)
>>>>> on this issue. ;-)
>>>>
>>>> Ironically the conversion to a double is a minor bug:
>>>>
>>>> Fraction.of(0, 1).doubleValue() == 0.0
>>>> Fraction.of(0, -01).doubleValue() == -0.0
>>>>
>>>> IEEE754 arithmetic for 0.0 / -1.0 creates a -0.0.
>>>>
>>>> Do we want to support -0.0?
>>>
>>> Why prevent it since it looks expected from the above call?
>>
>> Well, in the against argument -0.0 is an artefact of the IEEE floating point
>> format. It is not a real number.
>>
>> If we allow 0 / -1 as a fraction to mean something then we should really
>> support it fully which means carrying the sign of the denominator through
>> arithmetic as would be done for -0.0 (from the top of my head):
>>
>> -0.0 + -0.0 = -0.0
>> -0.0 + 0.0 = 0.0
>> 0.0 - -0.0 = 0.0
>> 0.0 - 0.0 = 0.0
>> 0.0 * 42 = 0.0
>> -0.0 * 42 = -0.0
>>
>> And so on...
>>
>> It is easier to exclude this representation from ever existing by changing
>> the factory constructor to not allow it.
>>
>> Note that Fraction.of(-0.0) creates 0 / 1. So the support for 0 / 1 is
>> inconsistent with conversion to and from double:
>>
>> Fraction.of(-0.0).doubleValue() == 0.0
>> Fraction.of(0, -1).doubleValue() == -0.0
>>
>> I have checked and Fraction.of(0, 1).compareTo(Fraction.of(0, -1)) is 0.
>> They evaluate to equal and have the same hash code. So this behaviour is
>> different from Double.compareTo, Double.equals and Double.hashCode which
>> distinguishes the two values.
>>
>> If fraction represented a signed number using the signed numerator and an
>> unsigned denominator, reduced to smallest form, then the representation of
>> zero is fixed. It would be 0 / 1 as you cannot have -0 as an integer.
>
> This seems to be the winning argument to transform all zero to
> canonical form.
I have updated Fraction and BigFraction to have a canonical form for Zero. Both
types pass the same tests for the constructor using a double and the
doubleValue() method does not return -0.0 for zero.
I tried to align the FractionTest and BigFractionTest to be more similar. This
should ease maintenance going forward.
I found that the use of a negative denominator had broken the compareTo
function. So I have fixed that.
The Fraction add/subtract methods were direct adaptions from the BigFraction
method. This was copied over from CM 3:
public Fraction add(final int i) {
return new Fraction(numerator + i * denominator, denominator);
}
This fails to detect overflow. So I have added tests for this and fixed the
implementation so it does not wrap the numerator (overflow).
All the fixes are small changes. One thing I noticed was that the constructor
using a double is limited to a maximum denominator of Integer.MAX_VALUE.
However with the support for Integer.MIN_VALUE as a denominator this
constructor does not allow for example:
@Test
public void test() {
Fraction f1 = Fraction.of(-1, Integer.MIN_VALUE);
Fraction f2 = Fraction.from(f1.doubleValue(), Integer.MIN_VALUE);
Assertions.assertEquals(f1, f2);
}
I will raise a ticket for this and investigate fixing it. The same limitation
is in BigFraction too.
Alex