Skip to content

IsEvenInteger(10) returns false and IsOddInteger(10) returns true for any integer with a trailing zero #104

Description

@matt-edmondson

What's wrong

The even and odd tests only look at the parity of the stored significand:

// PreciseNumber/PreciseNumber.cs:1048
public static bool IsEvenInteger(PreciseNumber value) => IsInteger(value) && value.Significand.IsEven;
// PreciseNumber/PreciseNumber.cs:1080
public static bool IsOddInteger(PreciseNumber value) => IsInteger(value) && !value.Significand.IsEven;

The sanitizing constructor strips trailing zeros. So 10 is stored as significand 1 at exponent 1, and 100 as 1 at exponent 2. The significand's parity is only the value's parity when Exponent == 0. When Exponent > 0 the value is a multiple of 10, so it is always even.

Failure scenario

PreciseNumber.IsEvenInteger(PreciseNumber.Parse("10", null));   // False (expected True)
PreciseNumber.IsOddInteger(PreciseNumber.Parse("10", null));    // True  (expected False)
PreciseNumber.IsOddInteger(PreciseNumber.Parse("100", null));   // True
PreciseNumber.IsEvenInteger(10.ToPreciseNumber());               // False
PreciseNumber.IsOddInteger(PreciseNumber.Parse("7", null) + PreciseNumber.Parse("3", null)); // True

Any generic INumber<T> code that branches on T.IsEvenInteger or T.IsOddInteger gets the wrong answer for 10, 30, 50, 100, 1000 and so on. Sign handling for negative bases in a generic power routine is one example. The existing tests only check 1 and 2, so they do not catch this.

Suggested fix

public static bool IsEvenInteger(PreciseNumber value) =>
    IsInteger(value) && (value.Exponent > 0 || value.Significand.IsEven);
public static bool IsOddInteger(PreciseNumber value) =>
    IsInteger(value) && value.Exponent == 0 && !value.Significand.IsEven;

Add tests for 10, 100, 30 and -20.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions