RealRepresenter.avt

Переключить прокрутку окна
Загрузить этот исходный код

/*
    Реализация среды исполнения языка программирования
    Объектно-ориентированный продвинутый векторный транслятор

    Copyright © 2021, 2024 Малик Разработчик

    Это свободная программа: вы можете перераспространять ее и/или изменять
    ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она будет полезной,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <https://www.gnu.org/licenses/>.
*/

package avt.lang.math;

import avt.lang.array.*;

public class RealRepresenter(Object, Math)
{
    public static final int MIN_ORDER_DIGITS          =  1;
    public static final int MAX_ORDER_DIGITS          =  4;
    public static final int REAL_ORDER_DIGITS         =  4;
    public static final int FLOAT_ORDER_DIGITS        =  2;
    public static final int DOUBLE_ORDER_DIGITS       =  3;
    public static final int MIN_SIGNIFICAND_DIGITS    =  2;
    public static final int MAX_SIGNIFICAND_DIGITS    = 20;
    public static final int REAL_SIGNIFICAND_DIGITS   = 20;
    public static final int FLOAT_SIGNIFICAND_DIGITS  =  8;
    public static final int DOUBLE_SIGNIFICAND_DIGITS = 17;

    private static final long2 MUL_MIN = new long2 { 0xccccccccccccccccL, 0x0cccccccccccccccL };

    public static real pow10(real value, int power) {
        if(power > 0)
        {
            for(; power >= 0x1000; power -= 0x1000) value *= 1e+4096;
            return value * tab_04_00(power) * tab_08_05(power) * tab_11_09(power);
        }
        if(power < 0)
        {
            for(power = -power; power >= 0x1000; power -= 0x1000) value /= 1e+4096;
            return value / tab_04_00(power) / tab_08_05(power) / tab_11_09(power);
        }
        return value;
    }

    protected static boolean equals(CharArray string0, String string1) {
        return string0 instanceof String ? string1.equals(string0) : string1.length == string0.length && string1.contentEquals(string0);
    }

    protected static long2 round(real value) {
        if(Real.isNaN(value)) return 0;
        if(value <= -0x1.0p+127) return Int128.MIN_VALUE;
        if(value >= +0x1.0p+127) return Int128.MAX_VALUE;
        long2 bits = Real.toLong2Bits(value);
        long2 result = Long2.setElement(bits, 1, 0);
        int exp = (int) bits[1];
        boolean negative = exp < 0;
        if((exp &= 0x7fff) >= 0x403e)
        {
            result = Int128.shiftLeft(result, exp - 0x403e);
        } else
        {
            long sig = (long) result;
            result = (sig >>> (0x403e - exp)) + ((sig >>> (0x403d - exp)) & 1);
        }
        return !negative ? result : Int128.neg(result);
    }

    private static real tab_04_00(int power) {
        switch(power & 0x1f)
        {
        case  0: return 1e+0000;
        case  1: return 1e+0001;
        case  2: return 1e+0002;
        case  3: return 1e+0003;
        case  4: return 1e+0004;
        case  5: return 1e+0005;
        case  6: return 1e+0006;
        case  7: return 1e+0007;
        case  8: return 1e+0008;
        case  9: return 1e+0009;
        case 10: return 1e+0010;
        case 11: return 1e+0011;
        case 12: return 1e+0012;
        case 13: return 1e+0013;
        case 14: return 1e+0014;
        case 15: return 1e+0015;
        case 16: return 1e+0016;
        case 17: return 1e+0017;
        case 18: return 1e+0018;
        case 19: return 1e+0019;
        case 20: return 1e+0020;
        case 21: return 1e+0021;
        case 22: return 1e+0022;
        case 23: return 1e+0023;
        case 24: return 1e+0024;
        case 25: return 1e+0025;
        case 26: return 1e+0026;
        case 27: return 1e+0027;
        case 28: return 1e+0028;
        case 29: return 1e+0029;
        case 30: return 1e+0030;
        default: return 1e+0031;
        }
    }

    private static real tab_08_05(int power) {
        switch(power >> 5 & 0x0f)
        {
        case  0: return 1e+0000;
        case  1: return 1e+0032;
        case  2: return 1e+0064;
        case  3: return 1e+0096;
        case  4: return 1e+0128;
        case  5: return 1e+0160;
        case  6: return 1e+0192;
        case  7: return 1e+0224;
        case  8: return 1e+0256;
        case  9: return 1e+0288;
        case 10: return 1e+0320;
        case 11: return 1e+0352;
        case 12: return 1e+0384;
        case 13: return 1e+0416;
        case 14: return 1e+0448;
        default: return 1e+0480;
        }
    }

    private static real tab_11_09(int power) {
        switch(power >> 9 & 0x07)
        {
        case  0: return 1e+0000;
        case  1: return 1e+0512;
        case  2: return 1e+1024;
        case  3: return 1e+1536;
        case  4: return 1e+2048;
        case  5: return 1e+2560;
        case  6: return 1e+3072;
        default: return 1e+3584;
        }
    }

    protected final boolean fldSigAll; /* обязательное требование вывода всех значащих цифр: fldSigAll ? "5.0000000" : "5.0" */
    protected final boolean fldOrdAll; /* обязательное требование вывода всех цифр порядка: fldOrdAll ? "5.0E+0010" : "5.0E+10" */
    protected final boolean fldSigSign; /* обязательное требование вывода знака перед мантиссой: fldSigSign ? "+5.0" : "5.0" */
    protected final boolean fldOrdSign; /* обязательное требование вывода знака перед порядком: fldOrdSign ? "5.0E+10" : "5.0E10" */
    protected final boolean fldExpForm; /* обязательное требование вывода порядка: fldExpForm ? "5.0E+0" : "5.0" */
    protected final int fldSigDigits; /* максимальное количество выводимых цифр мантиссы */
    protected final int fldOrdDigits; /* минимальное количество выводимых цифр порядка */
    protected final long2 fldMinRepresentValue; /* 10^(fldSigDigits-1) как int128 */
    protected final long2 fldMaxRepresentValue; /* (10^fldSigDigits)-1 как int128 */
    protected final real fldLimitValueWithFractialPart; /* 10^(fldSigDigits-1) */
    protected final real fldLimitValueWithoutExponent; /* 10^fldSigDigits */

    public (int sigDigits, int ordDigits): this(sigDigits, ordDigits, false, true, false, false, true) {  }

    public (int sigDigits, int ordDigits, boolean sigAll, boolean ordAll): this(sigDigits, ordDigits, sigAll, ordAll, false, false, true) {  }

    public (int sigDigits, int ordDigits, boolean sigAll, boolean ordAll, boolean expForm): this(sigDigits, ordDigits, sigAll, ordAll, expForm, false, true) {  }

    public (int sigDigits, int ordDigits, boolean sigAll, boolean ordAll, boolean expForm, boolean sigSign, boolean ordSign) {
        if(sigDigits < MIN_SIGNIFICAND_DIGITS) sigDigits = MIN_SIGNIFICAND_DIGITS;
        if(sigDigits > MAX_SIGNIFICAND_DIGITS) sigDigits = MAX_SIGNIFICAND_DIGITS;
        if(ordDigits < MIN_ORDER_DIGITS) ordDigits = MIN_ORDER_DIGITS;
        if(ordDigits > MAX_ORDER_DIGITS) ordDigits = MAX_ORDER_DIGITS;
        long2 maxValue = 1;
        for(int index = sigDigits - 1; index-- > 0; ) maxValue = Int128.mul(maxValue, 10);
        fldSigAll = sigAll;
        fldOrdAll = ordAll;
        fldSigSign = sigSign;
        fldOrdSign = ordSign;
        fldExpForm = expForm;
        fldSigDigits = sigDigits;
        fldOrdDigits = ordDigits;
        fldMinRepresentValue = maxValue;
        fldMaxRepresentValue = Int128.sub(Int128.mul(maxValue, 10), 1);
        fldLimitValueWithFractialPart = pow10(1, sigDigits - 1);
        fldLimitValueWithoutExponent = pow10(1, sigDigits);
    }

    public boolean equals(Object anot) {
        if(anot == this) return true;
        if(!(anot instanceof RealRepresenter)) return false;
        RealRepresenter repr = (RealRepresenter) anot;
        return
            fldSigAll == repr.fldSigAll &&
            fldOrdAll == repr.fldOrdAll &&
            fldSigSign == repr.fldSigSign &&
            fldOrdSign == repr.fldOrdSign &&
            fldExpForm == repr.fldExpForm &&
            fldSigDigits == repr.fldSigDigits &&
            fldOrdDigits == repr.fldOrdDigits
        ;
    }

    public int hashCode() {
        return
            (fldSigAll ? 0b00001 : 0) |
            (fldOrdAll ? 0b00010 : 0) |
            (fldSigSign ? 0b00100 : 0) |
            (fldOrdSign ? 0b01000 : 0) |
            (fldExpForm ? 0b10000 : 0) |
            (fldSigDigits - MIN_SIGNIFICAND_DIGITS << 8) |
            (fldOrdDigits - MIN_ORDER_DIGITS << 16)
        ;
    }

    public long hashCodeAsLong() {
        return
            (fldSigAll ? 0b00001 : 0) |
            (fldOrdAll ? 0b00010 : 0) |
            (fldSigSign ? 0b00100 : 0) |
            (fldOrdSign ? 0b01000 : 0) |
            (fldExpForm ? 0b10000 : 0) |
            (fldSigDigits - MIN_SIGNIFICAND_DIGITS << 8) |
            (fldOrdDigits - MIN_ORDER_DIGITS << 16)
        ;
    }

    public int writeTo(MutableCharArray dst, int offset, real value) {
        if(dst == null)
        {
            throw new NullPointerException(String.format(avt.lang.package.getResourceString("null-pointer.argument"), new Object[] { "dst" }));
        }
        char[] result = toCharArray(value);
        int length = result.length;
        if(dst instanceof char[])
        {
            Array.copy(result, 0, (char[]) dst, offset, length);
        }
        else for(int index = 0; index < length; offset++, index++)
        {
            dst[offset] = result[index];
        }
        return offset + length;
    }

    public float parseFloat(CharArray string) {
        if(string == null)
        {
            throw new NullPointerException(String.format(avt.lang.package.getResourceString("null-pointer.argument"), new Object[] { "string" }));
        }
        if(equals(string, "+Infinity") || equals(string, "Infinity")) return Float.POSITIVE_INFINITY;
        if(equals(string, "-Infinity")) return Float.NEGATIVE_INFINITY;
        if(equals(string, "NaN")) return Float.NAN;
        real result = parse(string);
        if(result < -Float.MAX_VALUE || result > Float.MAX_VALUE)
        {
            throw new NumberFormatException(avt.lang.package.getResourceString("illegal-argument.number-format"));
        }
        return (float) result;
    }

    public double parseDouble(CharArray string) {
        if(string == null)
        {
            throw new NullPointerException(String.format(avt.lang.package.getResourceString("null-pointer.argument"), new Object[] { "string" }));
        }
        if(equals(string, "+Infinity") || equals(string, "Infinity")) return Double.POSITIVE_INFINITY;
        if(equals(string, "-Infinity")) return Double.NEGATIVE_INFINITY;
        if(equals(string, "NaN")) return Double.NAN;
        real result = parse(string);
        if(result < -Double.MAX_VALUE || result > Double.MAX_VALUE)
        {
            throw new NumberFormatException(avt.lang.package.getResourceString("illegal-argument.number-format"));
        }
        return (double) result;
    }

    public real parseReal(CharArray string) {
        if(string == null)
        {
            throw new NullPointerException(String.format(avt.lang.package.getResourceString("null-pointer.argument"), new Object[] { "string" }));
        }
        if(equals(string, "+Infinity") || equals(string, "Infinity")) return Real.POSITIVE_INFINITY;
        if(equals(string, "-Infinity")) return Real.NEGATIVE_INFINITY;
        if(equals(string, "NaN")) return Real.NAN;
        real result = parse(string);
        if(result < -Real.MAX_VALUE || result > Real.MAX_VALUE)
        {
            throw new NumberFormatException(avt.lang.package.getResourceString("illegal-argument.number-format"));
        }
        return result;
    }

    public char[] toCharArray(real value) {
        return 
            Real.isNaN(value) ? "NaN".toCharArray() :
            value == Real.POSITIVE_INFINITY ? "Infinity".toCharArray() :
            value == Real.NEGATIVE_INFINITY ? "-Infinity".toCharArray() :
            represent(value)
        ;
    }

    public String toString(real value) {
        return
            Real.isNaN(value) ? "NaN" :
            value == Real.POSITIVE_INFINITY ? "Infinity" :
            value == Real.NEGATIVE_INFINITY ? "-Infinity" :
            new String(represent(value))
        ;
    }

    public boolean orderAll { read = fldOrdAll }

    public boolean orderSign { read = fldOrdSign }

    public boolean significandAll { read = fldSigAll }

    public boolean significandSign { read = fldSigSign }

    public boolean exponentialForm { read = fldExpForm }

    public int orderDigits { read = fldOrdDigits }

    public int significandDigits { read = fldSigDigits }

    protected real parse(CharArray string) {
        int length = string == null ? 0 : string.length;
        if(0 >= length)
        {
            throw new NumberFormatException(avt.lang.package.getResourceString("illegal-argument.number-format"));
        }
        boolean negative = false;
        int frac = 0;
        int order = 0;
        int index = 0;
        long2 intValue = 0;
        switch(string[0])
        {
        case '-':
            negative = true;
            /* падение через */
        case '+':
        case ' ':
            if(++index >= length)
            {
                throw new NumberFormatException(avt.lang.package.getResourceString("illegal-argument.number-format"));
            }
        }
        char character = string[index];
        if((character < '0' || character > '9') && character != '.')
        {
            throw new NumberFormatException(avt.lang.package.getResourceString("illegal-argument.number-format"));
        }
        if(character != '.') for(intValue = character - '0'; ++index < length && (character = string[index]) >= '0' && character <= '9'; )
        {
            long2 digit = character - '0';
            long2 temp = Int128.mul(intValue, 10);
            if(Int128.compare(intValue, MUL_MIN) > 0 || Int128.compare(temp, Int128.sub(Int128.MAX_VALUE, digit)) > 0)
            {
                frac--;
            } else
            {
                intValue = Int128.add(temp, digit);
            }
        }
        if(index < length && string[index] == '.') while(++index < length && (character = string[index]) >= '0' && character <= '9')
        {
            long2 digit = character - '0';
            long2 temp = Int128.mul(intValue, 10);
            if(Int128.compare(intValue, MUL_MIN) <= 0 && Int128.compare(temp, Int128.sub(Int128.MAX_VALUE, digit)) <= 0)
            {
                frac++;
                intValue = Int128.add(temp, digit);
            }
        }
        if(Int128.signum(intValue) > 0) while(frac > 0 && Int128.signum(Int128.rem(intValue, 10)) == 0)
        {
            frac--;
            intValue = Int128.div(intValue, 10);
        }
        real result = Int128.toReal(intValue);
        if(negative)
        {
            negative = false;
            result /= -1;
        }
        if(index < length && ((character = string[index]) == 'E' || character == 'e'))
        {
            if(++index >= length)
            {
                throw new NumberFormatException(avt.lang.package.getResourceString("illegal-argument.number-format"));
            }
            switch(string[index])
            {
            case '-':
                negative = true;
                /* падение через */
            case '+':
                if(++index >= length)
                {
                    throw new NumberFormatException(avt.lang.package.getResourceString("illegal-argument.number-format"));
                }
            }
            if((character = string[index]) < '0' || character > '9')
            {
                throw new NumberFormatException(avt.lang.package.getResourceString("illegal-argument.number-format"));
            }
            for(order = character - '0'; ++index < length && (character = string[index]) >= '0' && character <= '9'; ) if((order = 10 * order + character - '0') > 9999)
            {
                throw new NumberFormatException(avt.lang.package.getResourceString("illegal-argument.number-format"));
            }
            if(negative) order = -order;
        }
        if(index < length)
        {
            throw new NumberFormatException(avt.lang.package.getResourceString("illegal-argument.number-format"));
        }
        return pow10(result, order - frac);
    }

    protected char[] represent(real value) {
        char[] result = new char[32];
        Array.fill(result, 0, 32, '0');
        long2 bits = Real.toLong2Bits(value);
        int length = 0;
        int order = (int) bits[1];
        if(order < 0)
        {
            result[length++] = '-';
            value /= -1;
        }
        else if(fldSigSign)
        {
            result[length++] = '+';
        }
        if((bits & new short2 { -1, 0x7fff }) == 0)
        {
            result[length + 1] = '.';
            length += fldSigAll ? fldSigDigits + 1 : 3;
            if(fldExpForm)
            {
                result[length++] = 'E';
                if(fldOrdSign) result[length++] = '+';
                length += fldOrdAll ? fldOrdDigits : 1;
            }
        } else
        {
            order = (int) floor(log10(value));
            int sigDigits = fldSigDigits;
            real minValue = pow10(1, sigDigits < 6 ? -sigDigits : -6);
            boolean expForm = fldExpForm || value < minValue || value >= fldLimitValueWithoutExponent;
            long2 intValue;
            int dotIndex;
            if(expForm)
            {
                intValue = round(pow10(value, sigDigits - order - 1));
                if(Int128.compare(intValue, fldMinRepresentValue) < 0)
                {
                    intValue = Int128.mul(intValue, 10);
                    order--;
                }
                if(Int128.compare(intValue, fldMaxRepresentValue) > 0)
                {
                    intValue = Int128.div(intValue, 10);
                    order++;
                }
                dotIndex = length + 1;
            }
            else if(value < 1)
            {
                intValue = round(pow10(value, sigDigits - 1));
                dotIndex = length + 1;
            }
            else if(value < fldLimitValueWithFractialPart)
            {
                intValue = round(pow10(value, sigDigits - order - 1));
                if(Int128.compare(intValue, fldMinRepresentValue) < 0)
                {
                    intValue = Int128.mul(intValue, 10);
                    order--;
                }
                if(Int128.compare(intValue, fldMaxRepresentValue) > 0)
                {
                    intValue = Int128.div(intValue, 10);
                    order++;
                }
                dotIndex = length + order + 1;
            }
            else
            {
                intValue = round(value);
                long2 maxValue = fldMaxRepresentValue;
                if(Int128.compare(intValue, maxValue) > 0)
                {
                    intValue = maxValue;
                }
                dotIndex = length + sigDigits;
            }
            result[dotIndex] = '.';
            for(int index = length + sigDigits; index-- > length; intValue = Int128.div(intValue, 10))
            {
                result[index < dotIndex ? index : index + 1] = (char) ((int) Int128.rem(intValue, 10) + '0');
            }
            length += sigDigits + 1;
            if(!fldSigAll) for(; ; )
            {
                char character = result[length - 1];
                if(character != '0' && character != '.') break;
                length--;
                if(character == '.')
                {
                    length += 2;
                    break;
                }
            }
            if(expForm)
            {
                result[length++] = 'E';
                if(order < 0)
                {
                    result[length++] = '-';
                    order = -order;
                }
                else if(fldOrdSign)
                {
                    result[length++] = '+';
                }
                int ordDigits = fldOrdAll ? fldOrdDigits : 1;
                if(ordDigits == 1 && order >= 10) ordDigits++;
                if(ordDigits == 2 && order >= 100) ordDigits++;
                if(ordDigits == 3 && order >= 1000) ordDigits++;
                int ordLength = length + ordDigits;
                for(int index = ordLength; index-- > length; order /= 10)
                {
                    result[index] = (char) (order % 10 + '0');
                }
                length = ordLength;
            }
        }
        result.length = length;
        return result;
    }
}