On Sun Nov 6 00:17:19 UTC 2022 Johannes Kuhn <info at j-kuhn.de> wrote:

...
In particular, I would love to see the following methods added*:

- double Double.fromUnsignedLong(long i)
- long Double.toUnsignedLong(double d)
- float Float.fromUnsignedLong(long i)
- long Float.toUnsignedLong(float f)
...

The methods suggested for double to unsigned long by David Lloyd and Remi Forax differ from BigInteger.doubleValue() in a few cases because of rounding error.
 They also fail some WebAssembly tests
(https://github.com/WebAssembly/testsuite/blob/main/conversions.wast).

The double to unsigned long method below agrees with BigInteger.doubleValue() on random tests and
passes the WebAssembly tests.


    // DOUBLE 1 11 52
    private static final int PHYSICAL_MANTISSA = 52;
private static final int MANTISSA = PHYSICAL_MANTISSA + 1; // leading one bit not stored in normal numbers
    private static final int SHIFT_DOWN = 64 - MANTISSA;
    private static final long DIVISOR = 1L << SHIFT_DOWN;
    private static final long REMAINDER_MASK = DIVISOR - 1;
    private static final long HALF = DIVISOR >>> 1;
    private static final double MULTIPLIER = (double)DIVISOR;

    public static double doubleFromUnsignedLong(long value) {
        if (value >= 0) {
            return (double)value;
        }
long mantissa = value >>> SHIFT_DOWN; // top bit of mantissa is 1 as value negative
        long remainder = value & REMAINDER_MASK;
if (remainder > HALF || remainder == HALF && (mantissa & 1) != 0) { // round up (half to even) ?
            ++mantissa;
        }
        return MULTIPLIER * mantissa;
    }

    private final static double TWO63D = 0x1.0p63;
    private final static double TWO64D = 0x1.0p64;

    public static long doubleToUnsignedLongStrict(double x) {
        if (Double.isNaN(x)) {
            String msg = "INTEGER_CONVERSION";
            throw new ArithmeticException(msg);
        }
        if (x >= TWO64D || x <= -1.0) {
            String msg = "INTEGER_OVERFLOW";
            throw new ArithmeticException(msg);
        }
        if (x < TWO63D) {
            return (long)x;
        }
// x is an integer in [0x1.0p63,0x1.0p64) and last 11 bits are zero
        double y = x/2;
        long yl = (long)y;
        return  yl << 1;
    }

    // FLOAT 1 8 23
    private static final int PHYSICAL_MANTISSA_F = 23;
private static final int MANTISSA_F = PHYSICAL_MANTISSA_F + 1; // leading one bit not stored in normal numbers
    private static final int SHIFT_DOWN_F = 64 - MANTISSA_F;
    private static final long DIVISOR_F = 1L << SHIFT_DOWN_F;
    private static final long REMAINDER_MASK_F = DIVISOR_F - 1;
    private static final long HALF_F = DIVISOR_F >>> 1;
    private static final float MULTIPLIER_F = (float)DIVISOR_F;

    public static float floatFromUnsignedLong(long value) {
        if (value >= 0) {
            return (float)value;
        }
long mantissa = value >>> SHIFT_DOWN_F; // top bit of mantissa is 1 as value negative
        long remainder = value & REMAINDER_MASK_F;
if (remainder > HALF_F || remainder == HALF_F && (mantissa & 1) != 0) { // round up (half to even) ?
            ++mantissa;
        }
        return MULTIPLIER_F * mantissa;
    }

    private final static double TWO31D = 0x1.0p31;
    private final static double TWO32D = 0x1.0p32;

    public static int doubleToUnsignedIntStrict(double x) {
        if (Double.isNaN(x)) {
            String msg = "INTEGER_CONVERSION";
            throw new ArithmeticException(msg);
        }
        if (x >= TWO32D || x <= -1.0) {
            String msg = "INTEGER_OVERFLOW";
            throw new ArithmeticException(msg);
        }
        if (x < TWO31D) {
            return (int)x;
        }
        return (int)(long)x;
    }

Reply via email to