Skip to content

Half.operator % seems to randomly fail on some configuration #67993

Description

@tannergooding

#67939 exposed an issue where the test AssertBitwiseEqual(NegativeZero, ModulusOperatorsHelper<Half, Half, Half>.op_Modulus(NegativeZero, PositiveTwo)); failed with "expected -0, actual 0`.

Where:

private static Half NegativeZero => BitConverter.UInt16BitsToHalf(0x8000);
private static Half PositiveTwo => BitConverter.UInt16BitsToHalf(0x4000);

public static Half operator %(Half left, Half right) => (Half)((float)left % (float)right);

public static explicit operator Half(float value)
{
    const int SingleMaxExponent = 0xFF;

    uint floatInt = BitConverter.SingleToUInt32Bits(value);
    bool sign = (floatInt & float.SignMask) >> float.SignShift != 0;
    int exp = (int)(floatInt & float.ExponentMask) >> float.ExponentShift;
    uint sig = floatInt & float.SignificandMask;

    if (exp == SingleMaxExponent)
    {
        if (sig != 0) // NaN
        {
            return CreateHalfNaN(sign, (ulong)sig << 41); // Shift the significand bits to the left end
        }
        return sign ? NegativeInfinity : PositiveInfinity;
    }

    uint sigHalf = sig >> 9 | ((sig & 0x1FFU) != 0 ? 1U : 0U); // RightShiftJam

    if ((exp | (int)sigHalf) == 0)
    {
        return new Half(sign, 0, 0);
    }

    return new Half(RoundPackToHalf(sign, (short)(exp - 0x71), (ushort)(sigHalf | 0x4000)));
}

public static explicit operator float(Half value)
{
    bool sign = IsNegative(value);
    int exp = value.Exponent;
    uint sig = value.Significand;

    if (exp == MaxExponent)
    {
        if (sig != 0)
        {
            return CreateSingleNaN(sign, (ulong)sig << 54);
        }
        return sign ? float.NegativeInfinity : float.PositiveInfinity;
    }

    if (exp == 0)
    {
        if (sig == 0)
        {
            return BitConverter.UInt32BitsToSingle(sign ? float.SignMask : 0); // Positive / Negative zero
        }
        (exp, sig) = NormSubnormalF16Sig(sig);
        exp -= 1;
    }

    return CreateSingle(sign, (byte)(exp + 0x70), sig << 13);
}

Given this logic, the result should be -0 and is negative zero on most of the CI legs. However runtime (Libraries Test Run release coreclr windows x64 Debug) in particular failed citing the actual result was 0

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-System.Numericsin-prThere is an active PR which will close this issue when it is merged

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions