diff --git a/runtime/src/main/cpp/Exceptions.h b/runtime/src/main/cpp/Exceptions.h index eaf8345685a..effb8bf9018 100644 --- a/runtime/src/main/cpp/Exceptions.h +++ b/runtime/src/main/cpp/Exceptions.h @@ -42,6 +42,8 @@ void ThrowClassCastException(); void ThrowArithmeticException(); // Throws number format exception. void ThrowNumberFormatException(); +// Throws out of memory error. +void ThrowOutOfMemoryError(); #ifdef __cplusplus } // extern "C" diff --git a/runtime/src/main/cpp/KString.cpp b/runtime/src/main/cpp/KString.cpp index a7532def32c..3ca8a4c8469 100644 --- a/runtime/src/main/cpp/KString.cpp +++ b/runtime/src/main/cpp/KString.cpp @@ -703,33 +703,6 @@ void checkParsingErrors(const char* c_str, const char* end, std::string::size_ty } } -// TODO: Java Double.valueOf specification requires mandatory binary exponent character (p) in the string parsed if the string is a hex one. -// See: http://docs.oracle.com/javase/8/docs/api/java/lang/Double.html#valueOf-java.lang.String- -// E.g. -// "0x77p0".toDouble() // OK for both Kotlin/JVM and Kotlin/Native. -// "0x77".toDouble() // throws NumberFormatException in Kotlin/JVM and OK in Kotlin/Native. -// Do we need to handle such case? Or it is OK to consume such strings? -KFloat parseFloat(KString value) { - const KChar* utf16 = CharArrayAddressOfElementAt(value, 0); - std::string utf8; - utf8::utf16to8(utf16, utf16 + value->count_, back_inserter(utf8)); - char* end = nullptr; - KFloat result = strtof(utf8.c_str(), &end); - checkParsingErrors(utf8.c_str(), end, utf8.size()); - return result; -} - -KDouble parseDouble(KString value) { - const KChar* utf16 = - CharArrayAddressOfElementAt(value, 0); - std::string utf8; - utf8::utf16to8(utf16, utf16 + value->count_, back_inserter(utf8)); - char* end = nullptr; - KDouble result = strtod(utf8.c_str(), &end); - checkParsingErrors(utf8.c_str(), end, utf8.size()); - return result; -} - } // namespace extern "C" { @@ -1172,12 +1145,4 @@ OBJ_GETTER0(Kotlin_io_Console_readLine) { RETURN_RESULT_OF(CreateStringFromCString, data); } -KFloat Kotlin_String_parseFloat(KString value) { - return parseFloat(value); -} - -KDouble Kotlin_String_parseDouble(KString value) { - return parseDouble(value); -} - } // extern "C" diff --git a/runtime/src/main/cpp/dtoa/cbigint.cpp b/runtime/src/main/cpp/dtoa/cbigint.cpp new file mode 100644 index 00000000000..136196da0d3 --- /dev/null +++ b/runtime/src/main/cpp/dtoa/cbigint.cpp @@ -0,0 +1,904 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "cbigint.h" + +#if defined(LINUX) || defined(FREEBSD) || defined(ZOS) || defined(MACOSX) || defined(AIX) +#define USE_LL +#endif + +#ifdef HY_LITTLE_ENDIAN +#define at(i) (i) +#else +#define at(i) ((i)^1) +/* the sequence for halfAt is -1, 2, 1, 4, 3, 6, 5, 8... */ +/* and it should correspond to 0, 1, 2, 3, 4, 5, 6, 7... */ +#define halfAt(i) (-((-(i)) ^ 1)) +#endif + +#define HIGH_IN_U64(u64) ((u64) >> 32) +#if defined(USE_LL) +#define LOW_IN_U64(u64) ((u64) & 0x00000000FFFFFFFFLL) +#else +#if defined(USE_L) +#define LOW_IN_U64(u64) ((u64) & 0x00000000FFFFFFFFL) +#else +#define LOW_IN_U64(u64) ((u64) & 0x00000000FFFFFFFF) +#endif /* USE_L */ +#endif /* USE_LL */ + +#if defined(USE_LL) +#define TEN_E1 (0xALL) +#define TEN_E2 (0x64LL) +#define TEN_E3 (0x3E8LL) +#define TEN_E4 (0x2710LL) +#define TEN_E5 (0x186A0LL) +#define TEN_E6 (0xF4240LL) +#define TEN_E7 (0x989680LL) +#define TEN_E8 (0x5F5E100LL) +#define TEN_E9 (0x3B9ACA00LL) +#define TEN_E19 (0x8AC7230489E80000LL) +#else +#if defined(USE_L) +#define TEN_E1 (0xAL) +#define TEN_E2 (0x64L) +#define TEN_E3 (0x3E8L) +#define TEN_E4 (0x2710L) +#define TEN_E5 (0x186A0L) +#define TEN_E6 (0xF4240L) +#define TEN_E7 (0x989680L) +#define TEN_E8 (0x5F5E100L) +#define TEN_E9 (0x3B9ACA00L) +#define TEN_E19 (0x8AC7230489E80000L) +#else +#define TEN_E1 (0xA) +#define TEN_E2 (0x64) +#define TEN_E3 (0x3E8) +#define TEN_E4 (0x2710) +#define TEN_E5 (0x186A0) +#define TEN_E6 (0xF4240) +#define TEN_E7 (0x989680) +#define TEN_E8 (0x5F5E100) +#define TEN_E9 (0x3B9ACA00) +#define TEN_E19 (0x8AC7230489E80000) +#endif /* USE_L */ +#endif /* USE_LL */ + +#define TIMES_TEN(x) (((x) << 3) + ((x) << 1)) +#define bitSection(x, mask, shift) (((x) & (mask)) >> (shift)) +#define DOUBLE_TO_LONGBITS(dbl) (*((U_64 *)(&dbl))) +#define FLOAT_TO_INTBITS(flt) (*((U_32 *)(&flt))) +#define CREATE_DOUBLE_BITS(normalizedM, e) (((normalizedM) & MANTISSA_MASK) | (((U_64)((e) + E_OFFSET)) << 52)) + +#if defined(USE_LL) +#define MANTISSA_MASK (0x000FFFFFFFFFFFFFLL) +#define EXPONENT_MASK (0x7FF0000000000000LL) +#define NORMAL_MASK (0x0010000000000000LL) +#define SIGN_MASK (0x8000000000000000LL) +#else +#if defined(USE_L) +#define MANTISSA_MASK (0x000FFFFFFFFFFFFFL) +#define EXPONENT_MASK (0x7FF0000000000000L) +#define NORMAL_MASK (0x0010000000000000L) +#define SIGN_MASK (0x8000000000000000L) +#else +#define MANTISSA_MASK (0x000FFFFFFFFFFFFF) +#define EXPONENT_MASK (0x7FF0000000000000) +#define NORMAL_MASK (0x0010000000000000) +#define SIGN_MASK (0x8000000000000000) +#endif /* USE_L */ +#endif /* USE_LL */ + +#define E_OFFSET (1075) + +#define FLOAT_MANTISSA_MASK (0x007FFFFF) +#define FLOAT_EXPONENT_MASK (0x7F800000) +#define FLOAT_NORMAL_MASK (0x00800000) +#define FLOAT_E_OFFSET (150) + +IDATA +simpleAddHighPrecision (U_64 * arg1, IDATA length, U_64 arg2) +{ + /* assumes length > 0 */ + IDATA index = 1; + + *arg1 += arg2; + if (arg2 <= *arg1) + return 0; + else if (length == 1) + return 1; + + while (++arg1[index] == 0 && ++index < length); + + return (IDATA) index == length; +} + +IDATA +addHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2) +{ + /* addition is limited by length of arg1 as it this function is + * storing the result in arg1 */ + /* fix for cc (GCC) 3.2 20020903 (Red Hat Linux 8.0 3.2-7): code generated does not + * do the temp1 + temp2 + carry addition correct. carry is 64 bit because gcc has + * subtle issues when you mix 64 / 32 bit maths. */ + U_64 temp1, temp2, temp3; /* temporary variables to help the SH-4, and gcc */ + U_64 carry; + IDATA index; + + if (length1 == 0 || length2 == 0) + { + return 0; + } + else if (length1 < length2) + { + length2 = length1; + } + + carry = 0; + index = 0; + do + { + temp1 = arg1[index]; + temp2 = arg2[index]; + temp3 = temp1 + temp2; + arg1[index] = temp3 + carry; + if (arg2[index] < arg1[index]) + carry = 0; + else if (arg2[index] != arg1[index]) + carry = 1; + } + while (++index < length2); + if (!carry) + return 0; + else if (index == length1) + return 1; + + while (++arg1[index] == 0 && ++index < length1); + + return (IDATA) index == length1; +} + +void +subtractHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2) +{ + /* assumes arg1 > arg2 */ + IDATA index; + for (index = 0; index < length1; ++index) + arg1[index] = ~arg1[index]; + simpleAddHighPrecision (arg1, length1, 1); + + while (length2 > 0 && arg2[length2 - 1] == 0) + --length2; + + addHighPrecision (arg1, length1, arg2, length2); + + for (index = 0; index < length1; ++index) + arg1[index] = ~arg1[index]; + simpleAddHighPrecision (arg1, length1, 1); +} + +U_32 +simpleMultiplyHighPrecision (U_64 * arg1, IDATA length, U_64 arg2) +{ + /* assumes arg2 only holds 32 bits of information */ + U_64 product; + IDATA index; + + index = 0; + product = 0; + + do + { + product = + HIGH_IN_U64 (product) + arg2 * LOW_U32_FROM_PTR (arg1 + index); + LOW_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (product); + product = + HIGH_IN_U64 (product) + arg2 * HIGH_U32_FROM_PTR (arg1 + index); + HIGH_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (product); + } + while (++index < length); + + return HIGH_U32_FROM_VAR (product); +} + +void +simpleMultiplyAddHighPrecision (U_64 * arg1, IDATA length, U_64 arg2, + U_32 * result) +{ + /* Assumes result can hold the product and arg2 only holds 32 bits + of information */ + U_64 product; + IDATA index, resultIndex; + + index = resultIndex = 0; + product = 0; + + do + { + product = + HIGH_IN_U64 (product) + result[at (resultIndex)] + + arg2 * LOW_U32_FROM_PTR (arg1 + index); + result[at (resultIndex)] = LOW_U32_FROM_VAR (product); + ++resultIndex; + product = + HIGH_IN_U64 (product) + result[at (resultIndex)] + + arg2 * HIGH_U32_FROM_PTR (arg1 + index); + result[at (resultIndex)] = LOW_U32_FROM_VAR (product); + ++resultIndex; + } + while (++index < length); + + result[at (resultIndex)] += HIGH_U32_FROM_VAR (product); + if (result[at (resultIndex)] < HIGH_U32_FROM_VAR (product)) + { + /* must be careful with ++ operator and macro expansion */ + ++resultIndex; + while (++result[at (resultIndex)] == 0) + ++resultIndex; + } +} + +#ifndef HY_LITTLE_ENDIAN +void simpleMultiplyAddHighPrecisionBigEndianFix(U_64 *arg1, IDATA length, U_64 arg2, U_32 *result) { + /* Assumes result can hold the product and arg2 only holds 32 bits + of information */ + U_64 product; + IDATA index, resultIndex; + + index = resultIndex = 0; + product = 0; + + do { + product = HIGH_IN_U64(product) + result[halfAt(resultIndex)] + arg2 * LOW_U32_FROM_PTR(arg1 + index); + result[halfAt(resultIndex)] = LOW_U32_FROM_VAR(product); + ++resultIndex; + product = HIGH_IN_U64(product) + result[halfAt(resultIndex)] + arg2 * HIGH_U32_FROM_PTR(arg1 + index); + result[halfAt(resultIndex)] = LOW_U32_FROM_VAR(product); + ++resultIndex; + } while (++index < length); + + result[halfAt(resultIndex)] += HIGH_U32_FROM_VAR(product); + if (result[halfAt(resultIndex)] < HIGH_U32_FROM_VAR(product)) { + /* must be careful with ++ operator and macro expansion */ + ++resultIndex; + while (++result[halfAt(resultIndex)] == 0) ++resultIndex; + } +} +#endif + +void +multiplyHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2, + U_64 * result, IDATA length) +{ + /* assumes result is large enough to hold product */ + U_64 *temp; + U_32 *resultIn32; + IDATA count, index; + + if (length1 < length2) + { + temp = arg1; + arg1 = arg2; + arg2 = temp; + count = length1; + length1 = length2; + length2 = count; + } + + memset (result, 0, sizeof (U_64) * length); + + /* length1 > length2 */ + resultIn32 = (U_32 *) result; + index = -1; + for (count = 0; count < length2; ++count) + { + simpleMultiplyAddHighPrecision (arg1, length1, LOW_IN_U64 (arg2[count]), + resultIn32 + (++index)); +#ifdef HY_LITTLE_ENDIAN + simpleMultiplyAddHighPrecision(arg1, length1, HIGH_IN_U64(arg2[count]), resultIn32 + (++index)); +#else + simpleMultiplyAddHighPrecisionBigEndianFix(arg1, length1, HIGH_IN_U64(arg2[count]), resultIn32 + (++index)); +#endif + } +} + +U_32 +simpleAppendDecimalDigitHighPrecision (U_64 * arg1, IDATA length, U_64 digit) +{ + /* assumes digit is less than 32 bits */ + U_64 arg; + IDATA index = 0; + + digit <<= 32; + do + { + arg = LOW_IN_U64 (arg1[index]); + digit = HIGH_IN_U64 (digit) + TIMES_TEN (arg); + LOW_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (digit); + + arg = HIGH_IN_U64 (arg1[index]); + digit = HIGH_IN_U64 (digit) + TIMES_TEN (arg); + HIGH_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (digit); + } + while (++index < length); + + return HIGH_U32_FROM_VAR (digit); +} + +void +simpleShiftLeftHighPrecision (U_64 * arg1, IDATA length, IDATA arg2) +{ + /* assumes length > 0 */ + IDATA index, offset; + if (arg2 >= 64) + { + offset = arg2 >> 6; + index = length; + + while (--index - offset >= 0) + arg1[index] = arg1[index - offset]; + do + { + arg1[index] = 0; + } + while (--index >= 0); + + arg2 &= 0x3F; + } + + if (arg2 == 0) + return; + while (--length > 0) + { + arg1[length] = arg1[length] << arg2 | arg1[length - 1] >> (64 - arg2); + } + *arg1 <<= arg2; +} + +IDATA +highestSetBit (U_64 * y) +{ + U_32 x; + IDATA result; + + if (*y == 0) + return 0; + +#if defined(USE_LL) + if (*y & 0xFFFFFFFF00000000LL) + { + x = HIGH_U32_FROM_PTR (y); + result = 32; + } + else + { + x = LOW_U32_FROM_PTR (y); + result = 0; + } +#else +#if defined(USE_L) + if (*y & 0xFFFFFFFF00000000L) + { + x = HIGH_U32_FROM_PTR (y); + result = 32; + } + else + { + x = LOW_U32_FROM_PTR (y); + result = 0; + } +#else + if (*y & 0xFFFFFFFF00000000) + { + x = HIGH_U32_FROM_PTR (y); + result = 32; + } + else + { + x = LOW_U32_FROM_PTR (y); + result = 0; + } +#endif /* USE_L */ +#endif /* USE_LL */ + + if (x & 0xFFFF0000) + { + x = bitSection (x, 0xFFFF0000, 16); + result += 16; + } + if (x & 0xFF00) + { + x = bitSection (x, 0xFF00, 8); + result += 8; + } + if (x & 0xF0) + { + x = bitSection (x, 0xF0, 4); + result += 4; + } + if (x > 0x7) + return result + 4; + else if (x > 0x3) + return result + 3; + else if (x > 0x1) + return result + 2; + else + return result + 1; +} + +IDATA +lowestSetBit (U_64 * y) +{ + U_32 x; + IDATA result; + + if (*y == 0) + return 0; + +#if defined(USE_LL) + if (*y & 0x00000000FFFFFFFFLL) + { + x = LOW_U32_FROM_PTR (y); + result = 0; + } + else + { + x = HIGH_U32_FROM_PTR (y); + result = 32; + } +#else +#if defined(USE_L) + if (*y & 0x00000000FFFFFFFFL) + { + x = LOW_U32_FROM_PTR (y); + result = 0; + } + else + { + x = HIGH_U32_FROM_PTR (y); + result = 32; + } +#else + if (*y & 0x00000000FFFFFFFF) + { + x = LOW_U32_FROM_PTR (y); + result = 0; + } + else + { + x = HIGH_U32_FROM_PTR (y); + result = 32; + } +#endif /* USE_L */ +#endif /* USE_LL */ + + if (!(x & 0xFFFF)) + { + x = bitSection (x, 0xFFFF0000, 16); + result += 16; + } + if (!(x & 0xFF)) + { + x = bitSection (x, 0xFF00, 8); + result += 8; + } + if (!(x & 0xF)) + { + x = bitSection (x, 0xF0, 4); + result += 4; + } + + if (x & 0x1) + return result + 1; + else if (x & 0x2) + return result + 2; + else if (x & 0x4) + return result + 3; + else + return result + 4; +} + +IDATA +highestSetBitHighPrecision (U_64 * arg, IDATA length) +{ + IDATA highBit; + + while (--length >= 0) + { + highBit = highestSetBit (arg + length); + if (highBit) + return highBit + 64 * length; + } + + return 0; +} + +IDATA +lowestSetBitHighPrecision (U_64 * arg, IDATA length) +{ + IDATA lowBit, index = -1; + + while (++index < length) + { + lowBit = lowestSetBit (arg + index); + if (lowBit) + return lowBit + 64 * index; + } + + return 0; +} + +IDATA +compareHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2) +{ + while (--length1 >= 0 && arg1[length1] == 0); + while (--length2 >= 0 && arg2[length2] == 0); + + if (length1 > length2) + return 1; + else if (length1 < length2) + return -1; + else if (length1 > -1) + { + do + { + if (arg1[length1] > arg2[length1]) + return 1; + else if (arg1[length1] < arg2[length1]) + return -1; + } + while (--length1 >= 0); + } + + return 0; +} + +KDouble +toDoubleHighPrecision (U_64 * arg, IDATA length) +{ + IDATA highBit; + U_64 mantissa, test64; + U_32 test; + KDouble result; + + while (length > 0 && arg[length - 1] == 0) + --length; + + if (length == 0) + result = 0.0; + else if (length > 16) + { + DOUBLE_TO_LONGBITS (result) = EXPONENT_MASK; + } + else if (length == 1) + { + highBit = highestSetBit (arg); + if (highBit <= 53) + { + highBit = 53 - highBit; + mantissa = *arg << highBit; + DOUBLE_TO_LONGBITS (result) = + CREATE_DOUBLE_BITS (mantissa, -highBit); + } + else + { + highBit -= 53; + mantissa = *arg >> highBit; + DOUBLE_TO_LONGBITS (result) = + CREATE_DOUBLE_BITS (mantissa, highBit); + + /* perform rounding, round to even in case of tie */ + test = (LOW_U32_FROM_PTR (arg) << (11 - highBit)) & 0x7FF; + if (test > 0x400 || ((test == 0x400) && (mantissa & 1))) + DOUBLE_TO_LONGBITS (result) = DOUBLE_TO_LONGBITS (result) + 1; + } + } + else + { + highBit = highestSetBit (arg + (--length)); + if (highBit <= 53) + { + highBit = 53 - highBit; + if (highBit > 0) + { + mantissa = + (arg[length] << highBit) | (arg[length - 1] >> + (64 - highBit)); + } + else + { + mantissa = arg[length]; + } + DOUBLE_TO_LONGBITS (result) = + CREATE_DOUBLE_BITS (mantissa, length * 64 - highBit); + + /* perform rounding, round to even in case of tie */ + test64 = arg[--length] << highBit; + if (test64 > SIGN_MASK || ((test64 == SIGN_MASK) && (mantissa & 1))) + DOUBLE_TO_LONGBITS (result) = DOUBLE_TO_LONGBITS (result) + 1; + else if (test64 == SIGN_MASK) + { + while (--length >= 0) + { + if (arg[length] != 0) + { + DOUBLE_TO_LONGBITS (result) = + DOUBLE_TO_LONGBITS (result) + 1; + break; + } + } + } + } + else + { + highBit -= 53; + mantissa = arg[length] >> highBit; + DOUBLE_TO_LONGBITS (result) = + CREATE_DOUBLE_BITS (mantissa, length * 64 + highBit); + + /* perform rounding, round to even in case of tie */ + test = (LOW_U32_FROM_PTR (arg + length) << (11 - highBit)) & 0x7FF; + if (test > 0x400 || ((test == 0x400) && (mantissa & 1))) + DOUBLE_TO_LONGBITS (result) = DOUBLE_TO_LONGBITS (result) + 1; + else if (test == 0x400) + { + do + { + if (arg[--length] != 0) + { + DOUBLE_TO_LONGBITS (result) = + DOUBLE_TO_LONGBITS (result) + 1; + break; + } + } + while (length > 0); + } + } + } + + return result; +} + +IDATA +tenToTheEHighPrecision (U_64 * result, IDATA length, int e) +{ + /* size test */ + if (length < ((e / 19) + 1)) + return 0; + + memset (result, 0, length * sizeof (U_64)); + *result = 1; + + if (e == 0) + return 1; + + length = 1; + length = timesTenToTheEHighPrecision (result, length, e); + /* bad O(n) way of doing it, but simple */ + /* + do { + overflow = simpleAppendDecimalDigitHighPrecision(result, length, 0); + if (overflow) + result[length++] = overflow; + } while (--e); + */ + return length; +} + +IDATA +timesTenToTheEHighPrecision (U_64 * result, IDATA length, int e) +{ + /* assumes result can hold value */ + U_64 overflow; + int exp10 = e; + + if (e == 0) + return length; + + /* bad O(n) way of doing it, but simple */ + /* + do { + overflow = simpleAppendDecimalDigitHighPrecision(result, length, 0); + if (overflow) + result[length++] = overflow; + } while (--e); + */ + /* Replace the current implementation which performs a + * "multiplication" by 10 e number of times with an actual + * multiplication. 10e19 is the largest exponent to the power of ten + * that will fit in a 64-bit integer, and 10e9 is the largest exponent to + * the power of ten that will fit in a 64-bit integer. Not sure where the + * break-even point is between an actual multiplication and a + * simpleAappendDecimalDigit() so just pick 10e3 as that point for + * now. + */ + while (exp10 >= 19) + { + overflow = simpleMultiplyHighPrecision64 (result, length, TEN_E19); + if (overflow) + result[length++] = overflow; + exp10 -= 19; + } + while (exp10 >= 9) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E9); + if (overflow) + result[length++] = overflow; + exp10 -= 9; + } + if (exp10 == 0) + return length; + else if (exp10 == 1) + { + overflow = simpleAppendDecimalDigitHighPrecision (result, length, 0); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 2) + { + overflow = simpleAppendDecimalDigitHighPrecision (result, length, 0); + if (overflow) + result[length++] = overflow; + overflow = simpleAppendDecimalDigitHighPrecision (result, length, 0); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 3) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E3); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 4) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E4); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 5) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E5); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 6) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E6); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 7) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E7); + if (overflow) + result[length++] = overflow; + } + else if (exp10 == 8) + { + overflow = simpleMultiplyHighPrecision (result, length, TEN_E8); + if (overflow) + result[length++] = overflow; + } + return length; +} + +U_64 +doubleMantissa (KDouble z) +{ + U_64 m = DOUBLE_TO_LONGBITS (z); + + if ((m & EXPONENT_MASK) != 0) + m = (m & MANTISSA_MASK) | NORMAL_MASK; + else + m = (m & MANTISSA_MASK); + + return m; +} + +IDATA +doubleExponent (KDouble z) +{ + /* assumes positive double */ + IDATA k = HIGH_U32_FROM_VAR (z) >> 20; + + if (k) + k -= E_OFFSET; + else + k = 1 - E_OFFSET; + + return k; +} + +UDATA +floatMantissa (KFloat z) +{ + UDATA m = (UDATA) FLOAT_TO_INTBITS (z); + + if ((m & FLOAT_EXPONENT_MASK) != 0) + m = (m & FLOAT_MANTISSA_MASK) | FLOAT_NORMAL_MASK; + else + m = (m & FLOAT_MANTISSA_MASK); + + return m; +} + +IDATA +floatExponent (KFloat z) +{ + /* assumes positive float */ + IDATA k = FLOAT_TO_INTBITS (z) >> 23; + if (k) + k -= FLOAT_E_OFFSET; + else + k = 1 - FLOAT_E_OFFSET; + + return k; +} + +/* Allow a 64-bit value in arg2 */ +U_64 +simpleMultiplyHighPrecision64 (U_64 * arg1, IDATA length, U_64 arg2) +{ + U_64 intermediate, *pArg1, carry1, carry2, prod1, prod2, sum; + IDATA index; + U_32 buf32; + + index = 0; + intermediate = 0; + pArg1 = arg1 + index; + carry1 = carry2 = 0; + + do + { + if ((*pArg1 != 0) || (intermediate != 0)) + { + prod1 = + (U_64) LOW_U32_FROM_VAR (arg2) * (U_64) LOW_U32_FROM_PTR (pArg1); + sum = intermediate + prod1; + if ((sum < prod1) || (sum < intermediate)) + { + carry1 = 1; + } + else + { + carry1 = 0; + } + prod1 = + (U_64) LOW_U32_FROM_VAR (arg2) * (U_64) HIGH_U32_FROM_PTR (pArg1); + prod2 = + (U_64) HIGH_U32_FROM_VAR (arg2) * (U_64) LOW_U32_FROM_PTR (pArg1); + intermediate = carry2 + HIGH_IN_U64 (sum) + prod1 + prod2; + if ((intermediate < prod1) || (intermediate < prod2)) + { + carry2 = 1; + } + else + { + carry2 = 0; + } + LOW_U32_FROM_PTR (pArg1) = LOW_U32_FROM_VAR (sum); + buf32 = HIGH_U32_FROM_PTR (pArg1); + HIGH_U32_FROM_PTR (pArg1) = LOW_U32_FROM_VAR (intermediate); + intermediate = carry1 + HIGH_IN_U64 (intermediate) + + (U_64) HIGH_U32_FROM_VAR (arg2) * (U_64) buf32; + } + pArg1++; + } + while (++index < length); + return intermediate; +} diff --git a/runtime/src/main/cpp/dtoa/cbigint.h b/runtime/src/main/cpp/dtoa/cbigint.h new file mode 100644 index 00000000000..38a4c6403e9 --- /dev/null +++ b/runtime/src/main/cpp/dtoa/cbigint.h @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(cbigint_h) +#define cbigint_h +#include "fltconst.h" +#include "../Types.h" +//#include "vmi.h" +#define LOW_U32_FROM_VAR(u64) LOW_U32_FROM_LONG64(u64) +#define LOW_U32_FROM_PTR(u64ptr) LOW_U32_FROM_LONG64_PTR(u64ptr) +#define HIGH_U32_FROM_VAR(u64) HIGH_U32_FROM_LONG64(u64) +#define HIGH_U32_FROM_PTR(u64ptr) HIGH_U32_FROM_LONG64_PTR(u64ptr) +#if defined(__cplusplus) +extern "C" +{ +#endif + void multiplyHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, + IDATA length2, U_64 * result, IDATA length); + U_32 simpleAppendDecimalDigitHighPrecision (U_64 * arg1, IDATA length, U_64 digit); + KDouble toDoubleHighPrecision (U_64 * arg, IDATA length); + IDATA tenToTheEHighPrecision (U_64 * result, IDATA length, int e); + U_64 doubleMantissa (KDouble z); + IDATA compareHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2); + IDATA highestSetBitHighPrecision (U_64 * arg, IDATA length); + void subtractHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2); + IDATA doubleExponent (KDouble z); + U_32 simpleMultiplyHighPrecision (U_64 * arg1, IDATA length, U_64 arg2); + IDATA addHighPrecision (U_64 * arg1, IDATA length1, U_64 * arg2, IDATA length2); + void simpleMultiplyAddHighPrecisionBigEndianFix (U_64 * arg1, IDATA length, U_64 arg2, U_32 * result); + IDATA lowestSetBit (U_64 * y); + IDATA timesTenToTheEHighPrecision (U_64 * result, IDATA length, int e); + void simpleMultiplyAddHighPrecision (U_64 * arg1, IDATA length, U_64 arg2, U_32 * result); + IDATA highestSetBit (U_64 * y); + IDATA lowestSetBitHighPrecision (U_64 * arg, IDATA length); + void simpleShiftLeftHighPrecision (U_64 * arg1, IDATA length, IDATA arg2); + UDATA floatMantissa (KFloat z); + U_64 simpleMultiplyHighPrecision64 (U_64 * arg1, IDATA length, U_64 arg2); + IDATA simpleAddHighPrecision (U_64 * arg1, IDATA length, U_64 arg2); + IDATA floatExponent (KFloat z); +#if defined(__cplusplus) +} +#endif +#endif /* cbigint_h */ diff --git a/runtime/src/main/cpp/dtoa/dblparse.cpp b/runtime/src/main/cpp/dtoa/dblparse.cpp new file mode 100644 index 00000000000..eaf8f89e4a0 --- /dev/null +++ b/runtime/src/main/cpp/dtoa/dblparse.cpp @@ -0,0 +1,863 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "cbigint.h" +#include "../Natives.h" +#include "../Exceptions.h" +#include "../utf8.h" +#include +#include + +#if defined(LINUX) || defined(FREEBSD) || defined(ZOS) || defined(MACOSX) || defined(AIX) +#define USE_LL +#endif + +#define LOW_I32_FROM_VAR(u64) LOW_I32_FROM_LONG64(u64) +#define LOW_I32_FROM_PTR(u64ptr) LOW_I32_FROM_LONG64_PTR(u64ptr) +#define HIGH_I32_FROM_VAR(u64) HIGH_I32_FROM_LONG64(u64) +#define HIGH_I32_FROM_PTR(u64ptr) HIGH_I32_FROM_LONG64_PTR(u64ptr) + +#define MAX_ACCURACY_WIDTH 17 + +#define DEFAULT_WIDTH MAX_ACCURACY_WIDTH + +extern "C" { +KDouble Konan_FloatingPointParser_parseDoubleImpl (KString s, KInt e); + +void Konan_NumberConverter_bigIntDigitGeneratorInstImpl (KRef results, + KRef uArray, + KLong f, + KInt e, + KBoolean isDenormalized, + KBoolean mantissaIsZero, + KInt p); + +KDouble Konan_NumberConverter_ceil(KDouble x) { + return ceil(x); +} + +void Kotlin_IntArray_set(KRef thiz, KInt index, KInt value); +} + +KDouble createDouble (const char *s, KInt e); +KDouble createDouble1 (U_64 * f, IDATA length, KInt e); +KDouble doubleAlgorithm (U_64 * f, IDATA length, KInt e, KDouble z); + +U_64 dblparse_shiftRight64 (U_64 * lp, volatile int mbe); + +static const KDouble tens[] = { + 1.0, + 1.0e1, + 1.0e2, + 1.0e3, + 1.0e4, + 1.0e5, + 1.0e6, + 1.0e7, + 1.0e8, + 1.0e9, + 1.0e10, + 1.0e11, + 1.0e12, + 1.0e13, + 1.0e14, + 1.0e15, + 1.0e16, + 1.0e17, + 1.0e18, + 1.0e19, + 1.0e20, + 1.0e21, + 1.0e22 +}; + +#define tenToTheE(e) (*(tens + (e))) +#define LOG5_OF_TWO_TO_THE_N 23 +#define INV_LOG_OF_TEN_BASE_2 (0.30102999566398114) +#define DOUBLE_MIN_VALUE 5.0e-324 + +#define sizeOfTenToTheE(e) (((e) / 19) + 1) + +#if defined(USE_LL) +#define INFINITE_LONGBITS (0x7FF0000000000000LL) +#else +#if defined(USE_L) +#define INFINITE_LONGBITS (0x7FF0000000000000L) +#else +#define INFINITE_LONGBITS (0x7FF0000000000000) +#endif /* USE_L */ +#endif /* USE_LL */ + +#define MINIMUM_LONGBITS (0x1) + +#if defined(USE_LL) +#define MANTISSA_MASK (0x000FFFFFFFFFFFFFLL) +#define EXPONENT_MASK (0x7FF0000000000000LL) +#define NORMAL_MASK (0x0010000000000000LL) +#else +#if defined(USE_L) +#define MANTISSA_MASK (0x000FFFFFFFFFFFFFL) +#define EXPONENT_MASK (0x7FF0000000000000L) +#define NORMAL_MASK (0x0010000000000000L) +#else +#define MANTISSA_MASK (0x000FFFFFFFFFFFFF) +#define EXPONENT_MASK (0x7FF0000000000000) +#define NORMAL_MASK (0x0010000000000000) +#endif /* USE_L */ +#endif /* USE_LL */ + +#define DOUBLE_TO_LONGBITS(dbl) (*((U_64 *)(&dbl))) + +/* Keep a count of the number of times we decrement and increment to + * approximate the double, and attempt to detect the case where we + * could potentially toggle back and forth between decrementing and + * incrementing. It is possible for us to be stuck in the loop when + * incrementing by one or decrementing by one may exceed or stay below + * the value that we are looking for. In this case, just break out of + * the loop if we toggle between incrementing and decrementing for more + * than twice. + */ +#define INCREMENT_DOUBLE(_x, _decCount, _incCount) \ + { \ + ++DOUBLE_TO_LONGBITS(_x); \ + _incCount++; \ + if( (_incCount > 2) && (_decCount > 2) ) { \ + if( _decCount > _incCount ) { \ + DOUBLE_TO_LONGBITS(_x) += _decCount - _incCount; \ + } else if( _incCount > _decCount ) { \ + DOUBLE_TO_LONGBITS(_x) -= _incCount - _decCount; \ + } \ + break; \ + } \ + } +#define DECREMENT_DOUBLE(_x, _decCount, _incCount) \ + { \ + --DOUBLE_TO_LONGBITS(_x); \ + _decCount++; \ + if( (_incCount > 2) && (_decCount > 2) ) { \ + if( _decCount > _incCount ) { \ + DOUBLE_TO_LONGBITS(_x) += _decCount - _incCount; \ + } else if( _incCount > _decCount ) { \ + DOUBLE_TO_LONGBITS(_x) -= _incCount - _decCount; \ + } \ + break; \ + } \ + } +#define ERROR_OCCURED(x) (HIGH_I32_FROM_VAR(x) < 0) + +#define allocateU64(x, n) if (!((x) = (U_64*) malloc((n) * sizeof(U_64)))) goto OutOfMemory; +#define release(r) if ((r)) free((r)); + +/*NB the Number converter methods are synchronized so it is possible to + *have global data for use by bigIntDigitGenerator */ +#define RM_SIZE 21 +#define STemp_SIZE 22 + +KDouble createDouble (const char *s, KInt e) +{ + /* assumes s is a null terminated string with at least one + * character in it */ + U_64 def[DEFAULT_WIDTH]; + U_64 defBackup[DEFAULT_WIDTH]; + U_64 *f, *fNoOverflow, *g, *tempBackup; + U_32 overflow; + KDouble result; + IDATA index = 1; + int unprocessedDigits = 0; + + f = def; + fNoOverflow = defBackup; + *f = 0; + tempBackup = g = 0; + do + { + if (*s >= '0' && *s <= '9') + { + /* Make a back up of f before appending, so that we can + * back out of it if there is no more room, i.e. index > + * MAX_ACCURACY_WIDTH. + */ + memcpy (fNoOverflow, f, sizeof (U_64) * index); + overflow = + simpleAppendDecimalDigitHighPrecision (f, index, *s - '0'); + if (overflow) + { + f[index++] = overflow; + /* There is an overflow, but there is no more room + * to store the result. We really only need the top 52 + * bits anyway, so we must back out of the overflow, + * and ignore the rest of the string. + */ + if (index >= MAX_ACCURACY_WIDTH) + { + index--; + memcpy (f, fNoOverflow, sizeof (U_64) * index); + break; + } + if (tempBackup) + { + fNoOverflow = tempBackup; + } + } + } + else + index = -1; + } + while (index > 0 && *(++s) != '\0'); + + /* We've broken out of the parse loop either because we've reached + * the end of the string or we've overflowed the maximum accuracy + * limit of a double. If we still have unprocessed digits in the + * given string, then there are three possible results: + * 1. (unprocessed digits + e) == 0, in which case we simply + * convert the existing bits that are already parsed + * 2. (unprocessed digits + e) < 0, in which case we simply + * convert the existing bits that are already parsed along + * with the given e + * 3. (unprocessed digits + e) > 0 indicates that the value is + * simply too big to be stored as a double, so return Infinity + */ + if ((unprocessedDigits = strlen (s)) > 0) + { + e += unprocessedDigits; + if (index > -1) + { + if (e == 0) + result = toDoubleHighPrecision (f, index); + else if (e < 0) + result = createDouble1 (f, index, e); + else + { + DOUBLE_TO_LONGBITS (result) = INFINITE_LONGBITS; + } + } + else + { + LOW_I32_FROM_VAR (result) = -1; + HIGH_I32_FROM_VAR (result) = -1; + } + } + else + { + if (index > -1) + { + if (e == 0) + result = toDoubleHighPrecision (f, index); + else + result = createDouble1 (f, index, e); + } + else + { + LOW_I32_FROM_VAR (result) = -1; + HIGH_I32_FROM_VAR (result) = -1; + } + } + + return result; + +} + +KDouble +createDouble1 (U_64 * f, IDATA length, KInt e) +{ + IDATA numBits; + KDouble result; + +#define APPROX_MIN_MAGNITUDE -309 + +#define APPROX_MAX_MAGNITUDE 309 + + numBits = highestSetBitHighPrecision (f, length) + 1; + numBits -= lowestSetBitHighPrecision (f, length); + if (numBits < 54 && e >= 0 && e < LOG5_OF_TWO_TO_THE_N) + { + return toDoubleHighPrecision (f, length) * tenToTheE (e); + } + else if (numBits < 54 && e < 0 && (-e) < LOG5_OF_TWO_TO_THE_N) + { + return toDoubleHighPrecision (f, length) / tenToTheE (-e); + } + else if (e >= 0 && e < APPROX_MAX_MAGNITUDE) + { + result = toDoubleHighPrecision (f, length) * pow (10.0, (double) e); + } + else if (e >= APPROX_MAX_MAGNITUDE) + { + /* Convert the partial result to make sure that the + * non-exponential part is not zero. This check fixes the case + * where the user enters 0.0e309! */ + result = toDoubleHighPrecision (f, length); + /* Don't go straight to zero as the fact that x*0 = 0 independent of x might + cause the algorithm to produce an incorrect result. Instead try the min value + first and let it fall to zero if need be. */ + + if (result == 0.0) + + DOUBLE_TO_LONGBITS (result) = MINIMUM_LONGBITS; + else + DOUBLE_TO_LONGBITS (result) = INFINITE_LONGBITS; + } + else if (e > APPROX_MIN_MAGNITUDE) + { + result = toDoubleHighPrecision (f, length) / pow (10.0, (double) -e); + } + + if (e <= APPROX_MIN_MAGNITUDE) + { + + result = toDoubleHighPrecision (f, length) * pow (10.0, (double) (e + 52)); + result = result * pow (10.0, (double) -52); + + } + + /* Don't go straight to zero as the fact that x*0 = 0 independent of x might + cause the algorithm to produce an incorrect result. Instead try the min value + first and let it fall to zero if need be. */ + + if (result == 0.0) + + DOUBLE_TO_LONGBITS (result) = MINIMUM_LONGBITS; + + return doubleAlgorithm (f, length, e, result); +} + +U_64 +dblparse_shiftRight64 (U_64 * lp, volatile int mbe) +{ + U_64 b1Value = 0; + U_32 hi = HIGH_U32_FROM_LONG64_PTR (lp); + U_32 lo = LOW_U32_FROM_LONG64_PTR (lp); + int srAmt; + + if (mbe == 0) + return 0; + if (mbe >= 128) + { + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + LOW_U32_FROM_LONG64_PTR (lp) = 0; + return 0; + } + + /* Certain platforms do not handle de-referencing a 64-bit value + * from a pointer on the stack correctly (e.g. MVL-hh/XScale) + * because the pointer may not be properly aligned, so we'll have + * to handle two 32-bit chunks. */ + if (mbe < 32) + { + LOW_U32_FROM_LONG64 (b1Value) = 0; + HIGH_U32_FROM_LONG64 (b1Value) = lo << (32 - mbe); + LOW_U32_FROM_LONG64_PTR (lp) = (hi << (32 - mbe)) | (lo >> mbe); + HIGH_U32_FROM_LONG64_PTR (lp) = hi >> mbe; + } + else if (mbe == 32) + { + LOW_U32_FROM_LONG64 (b1Value) = 0; + HIGH_U32_FROM_LONG64 (b1Value) = lo; + LOW_U32_FROM_LONG64_PTR (lp) = hi; + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + } + else if (mbe < 64) + { + srAmt = mbe - 32; + LOW_U32_FROM_LONG64 (b1Value) = lo << (32 - srAmt); + HIGH_U32_FROM_LONG64 (b1Value) = (hi << (32 - srAmt)) | (lo >> srAmt); + LOW_U32_FROM_LONG64_PTR (lp) = hi >> srAmt; + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + } + else if (mbe == 64) + { + LOW_U32_FROM_LONG64 (b1Value) = lo; + HIGH_U32_FROM_LONG64 (b1Value) = hi; + LOW_U32_FROM_LONG64_PTR (lp) = 0; + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + } + else if (mbe < 96) + { + srAmt = mbe - 64; + b1Value = *lp; + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + LOW_U32_FROM_LONG64_PTR (lp) = 0; + LOW_U32_FROM_LONG64 (b1Value) >>= srAmt; + LOW_U32_FROM_LONG64 (b1Value) |= (hi << (32 - srAmt)); + HIGH_U32_FROM_LONG64 (b1Value) >>= srAmt; + } + else if (mbe == 96) + { + LOW_U32_FROM_LONG64 (b1Value) = hi; + HIGH_U32_FROM_LONG64 (b1Value) = 0; + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + LOW_U32_FROM_LONG64_PTR (lp) = 0; + } + else + { + LOW_U32_FROM_LONG64 (b1Value) = hi >> (mbe - 96); + HIGH_U32_FROM_LONG64 (b1Value) = 0; + HIGH_U32_FROM_LONG64_PTR (lp) = 0; + LOW_U32_FROM_LONG64_PTR (lp) = 0; + } + + return b1Value; +} + +#if defined(WIN32) +/* disable global optimizations on the microsoft compiler for the + * doubleAlgorithm function otherwise it won't compile */ +#pragma optimize("g",off) +#endif + +/* The algorithm for the function doubleAlgorithm() below can be found + * in: + * + * "How to Read Floating-Point Numbers Accurately", William D. + * Clinger, Proceedings of the ACM SIGPLAN '90 Conference on + * Programming Language Design and Implementation, June 20-22, + * 1990, pp. 92-101. + * + * There is a possibility that the function will end up in an endless + * loop if the given approximating floating-point number (a very small + * floating-point whose value is very close to zero) straddles between + * two approximating integer values. We modified the algorithm slightly + * to detect the case where it oscillates back and forth between + * incrementing and decrementing the floating-point approximation. It + * is currently set such that if the oscillation occurs more than twice + * then return the original approximation. + */ +KDouble doubleAlgorithm (U_64 * f, IDATA length, KInt e, KDouble z) +{ + U_64 m; + IDATA k, comparison, comparison2; + U_64 *x, *y, *D, *D2; + IDATA xLength, yLength, DLength, D2Length, decApproxCount, incApproxCount; + //PORT_ACCESS_FROM_ENV (env); + + x = y = D = D2 = 0; + xLength = yLength = DLength = D2Length = 0; + decApproxCount = incApproxCount = 0; + + do + { + m = doubleMantissa (z); + k = doubleExponent (z); + + if (x && x != f) + //jclmem_free_memory (env, x); + release(x); + release (y); + release (D); + release (D2); + + if (e >= 0 && k >= 0) + { + xLength = sizeOfTenToTheE (e) + length; + allocateU64 (x, xLength); + memset (x + length, 0, sizeof (U_64) * (xLength - length)); + memcpy (x, f, sizeof (U_64) * length); + timesTenToTheEHighPrecision (x, xLength, e); + + yLength = (k >> 6) + 2; + allocateU64 (y, yLength); + memset (y + 1, 0, sizeof (U_64) * (yLength - 1)); + *y = m; + simpleShiftLeftHighPrecision (y, yLength, k); + } + else if (e >= 0) + { + xLength = sizeOfTenToTheE (e) + length + ((-k) >> 6) + 1; + allocateU64 (x, xLength); + memset (x + length, 0, sizeof (U_64) * (xLength - length)); + memcpy (x, f, sizeof (U_64) * length); + timesTenToTheEHighPrecision (x, xLength, e); + simpleShiftLeftHighPrecision (x, xLength, -k); + + yLength = 1; + allocateU64 (y, 1); + *y = m; + } + else if (k >= 0) + { + xLength = length; + x = f; + + yLength = sizeOfTenToTheE (-e) + 2 + (k >> 6); + allocateU64 (y, yLength); + memset (y + 1, 0, sizeof (U_64) * (yLength - 1)); + *y = m; + timesTenToTheEHighPrecision (y, yLength, -e); + simpleShiftLeftHighPrecision (y, yLength, k); + } + else + { + xLength = length + ((-k) >> 6) + 1; + allocateU64 (x, xLength); + memset (x + length, 0, sizeof (U_64) * (xLength - length)); + memcpy (x, f, sizeof (U_64) * length); + simpleShiftLeftHighPrecision (x, xLength, -k); + + yLength = sizeOfTenToTheE (-e) + 1; + allocateU64 (y, yLength); + memset (y + 1, 0, sizeof (U_64) * (yLength - 1)); + *y = m; + timesTenToTheEHighPrecision (y, yLength, -e); + } + + comparison = compareHighPrecision (x, xLength, y, yLength); + if (comparison > 0) + { /* x > y */ + DLength = xLength; + allocateU64 (D, DLength); + memcpy (D, x, DLength * sizeof (U_64)); + subtractHighPrecision (D, DLength, y, yLength); + } + else if (comparison) + { /* y > x */ + DLength = yLength; + allocateU64 (D, DLength); + memcpy (D, y, DLength * sizeof (U_64)); + subtractHighPrecision (D, DLength, x, xLength); + } + else + { /* y == x */ + DLength = 1; + allocateU64 (D, 1); + *D = 0; + } + + D2Length = DLength + 1; + allocateU64 (D2, D2Length); + m <<= 1; + multiplyHighPrecision (D, DLength, &m, 1, D2, D2Length); + m >>= 1; + + comparison2 = compareHighPrecision (D2, D2Length, y, yLength); + if (comparison2 < 0) + { + if (comparison < 0 && m == NORMAL_MASK) + { + simpleShiftLeftHighPrecision (D2, D2Length, 1); + if (compareHighPrecision (D2, D2Length, y, yLength) > 0) + { + DECREMENT_DOUBLE (z, decApproxCount, incApproxCount); + } + else + { + break; + } + } + else + { + break; + } + } + else if (comparison2 == 0) + { + if ((LOW_U32_FROM_VAR (m) & 1) == 0) + { + if (comparison < 0 && m == NORMAL_MASK) + { + DECREMENT_DOUBLE (z, decApproxCount, incApproxCount); + } + else + { + break; + } + } + else if (comparison < 0) + { + DECREMENT_DOUBLE (z, decApproxCount, incApproxCount); + break; + } + else + { + INCREMENT_DOUBLE (z, decApproxCount, incApproxCount); + break; + } + } + else if (comparison < 0) + { + DECREMENT_DOUBLE (z, decApproxCount, incApproxCount); + } + else + { + if (DOUBLE_TO_LONGBITS (z) == INFINITE_LONGBITS) + break; + INCREMENT_DOUBLE (z, decApproxCount, incApproxCount); + } + } + while (1); + + if (x && x != f) + //jclmem_free_memory (env, x); + release(x); + release (y); + release (D); + release (D2); + return z; + +OutOfMemory: + if (x && x != f) + //jclmem_free_memory (env, x); + release(x); + release (y); + release (D); + release (D2); + + DOUBLE_TO_LONGBITS (z) = -2; + + return z; +} + +#if defined(WIN32) +#pragma optimize("",on) /*restore optimizations */ +#endif + +KDouble Konan_FloatingPointParser_parseDoubleImpl (KString s, KInt e) +{ + const KChar* utf16 = CharArrayAddressOfElementAt(s, 0); + std::string utf8; + utf8::utf16to8(utf16, utf16 + s->count_, back_inserter(utf8)); + const char *str = utf8.c_str(); + auto dbl = createDouble (str, e); + + if (!ERROR_OCCURED (dbl)) + { + return dbl; + } + else if (LOW_I32_FROM_VAR (dbl) == (I_32) - 1) + { /* NumberFormatException */ + ThrowNumberFormatException(); + } + else + { /* OutOfMemoryError */ + ThrowOutOfMemoryError(); + } + + return 0.0; +} + +/* The algorithm for this particular function can be found in: + * + * Printing Floating-Point Numbers Quickly and Accurately, Robert + * G. Burger, and R. Kent Dybvig, Programming Language Design and + * Implementation (PLDI) 1996, pp.108-116. + * + * The previous implementation of this function combined m+ and m- into + * one single M which caused some inaccuracy of the last digit. The + * particular case below shows this inaccuracy: + * + * System.out.println(new Double((1.234123412431233E107)).toString()); + * System.out.println(new Double((1.2341234124312331E107)).toString()); + * System.out.println(new Double((1.2341234124312332E107)).toString()); + * + * outputs the following: + * + * 1.234123412431233E107 + * 1.234123412431233E107 + * 1.234123412431233E107 + * + * instead of: + * + * 1.234123412431233E107 + * 1.2341234124312331E107 + * 1.2341234124312331E107 + * + */ +void Konan_NumberConverter_bigIntDigitGeneratorInstImpl (KRef results, + KRef uArray, + KLong f, + KInt e, + KBoolean isDenormalized, + KBoolean mantissaIsZero, + KInt p) +{ + int RLength, SLength, TempLength, mplus_Length, mminus_Length; + int high, low, i; + int k, firstK, U; + int getCount, setCount; + + U_64 R[RM_SIZE], S[STemp_SIZE], mplus[RM_SIZE], mminus[RM_SIZE], Temp[STemp_SIZE]; + + memset (R, 0, RM_SIZE * sizeof (U_64)); + memset (S, 0, STemp_SIZE * sizeof (U_64)); + memset (mplus, 0, RM_SIZE * sizeof (U_64)); + memset (mminus, 0, RM_SIZE * sizeof (U_64)); + memset (Temp, 0, STemp_SIZE * sizeof (U_64)); + + if (e >= 0) + { + *R = f; + *mplus = *mminus = 1; + simpleShiftLeftHighPrecision (mminus, RM_SIZE, e); + if (f != (2 << (p - 1))) + { + simpleShiftLeftHighPrecision (R, RM_SIZE, e + 1); + *S = 2; + /* + * m+ = m+ << e results in 1.0e23 to be printed as + * 0.9999999999999999E23 + * m+ = m+ << e+1 results in 1.0e23 to be printed as + * 1.0e23 (caused too much rounding) + * 470fffffffffffff = 2.0769187434139308E34 + * 4710000000000000 = 2.076918743413931E34 + */ + simpleShiftLeftHighPrecision (mplus, RM_SIZE, e); + } + else + { + simpleShiftLeftHighPrecision (R, RM_SIZE, e + 2); + *S = 4; + simpleShiftLeftHighPrecision (mplus, RM_SIZE, e + 1); + } + } + else + { + if (isDenormalized || (f != (2 << (p - 1)))) + { + *R = f << 1; + *S = 1; + simpleShiftLeftHighPrecision (S, STemp_SIZE, 1 - e); + *mplus = *mminus = 1; + } + else + { + *R = f << 2; + *S = 1; + simpleShiftLeftHighPrecision (S, STemp_SIZE, 2 - e); + *mplus = 2; + *mminus = 1; + } + } + + k = (int) ceil ((e + p - 1) * INV_LOG_OF_TEN_BASE_2 - 1e-10); + + if (k > 0) + { + timesTenToTheEHighPrecision (S, STemp_SIZE, k); + } + else + { + timesTenToTheEHighPrecision (R, RM_SIZE, -k); + timesTenToTheEHighPrecision (mplus, RM_SIZE, -k); + timesTenToTheEHighPrecision (mminus, RM_SIZE, -k); + } + + RLength = mplus_Length = mminus_Length = RM_SIZE; + SLength = TempLength = STemp_SIZE; + + memset (Temp + RM_SIZE, 0, (STemp_SIZE - RM_SIZE) * sizeof (U_64)); + memcpy (Temp, R, RM_SIZE * sizeof (U_64)); + + while (RLength > 1 && R[RLength - 1] == 0) + --RLength; + while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0) + --mplus_Length; + while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0) + --mminus_Length; + while (SLength > 1 && S[SLength - 1] == 0) + --SLength; + TempLength = (RLength > mplus_Length ? RLength : mplus_Length) + 1; + addHighPrecision (Temp, TempLength, mplus, mplus_Length); + + if (compareHighPrecision (Temp, TempLength, S, SLength) >= 0) + { + firstK = k; + } + else + { + firstK = k - 1; + simpleAppendDecimalDigitHighPrecision (R, ++RLength, 0); + simpleAppendDecimalDigitHighPrecision (mplus, ++mplus_Length, 0); + simpleAppendDecimalDigitHighPrecision (mminus, ++mminus_Length, 0); + while (RLength > 1 && R[RLength - 1] == 0) + --RLength; + while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0) + --mplus_Length; + while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0) + --mminus_Length; + } + + getCount = setCount = 0; + do + { + U = 0; + for (i = 3; i >= 0; --i) + { + TempLength = SLength + 1; + Temp[SLength] = 0; + memcpy (Temp, S, SLength * sizeof (U_64)); + simpleShiftLeftHighPrecision (Temp, TempLength, i); + if (compareHighPrecision (R, RLength, Temp, TempLength) >= 0) + { + subtractHighPrecision (R, RLength, Temp, TempLength); + U += 1 << i; + } + } + + low = compareHighPrecision (R, RLength, mminus, mminus_Length) <= 0; + + memset (Temp + RLength, 0, (STemp_SIZE - RLength) * sizeof (U_64)); + memcpy (Temp, R, RLength * sizeof (U_64)); + TempLength = (RLength > mplus_Length ? RLength : mplus_Length) + 1; + addHighPrecision (Temp, TempLength, mplus, mplus_Length); + + high = compareHighPrecision (Temp, TempLength, S, SLength) >= 0; + + if (low || high) + break; + + simpleAppendDecimalDigitHighPrecision (R, ++RLength, 0); + simpleAppendDecimalDigitHighPrecision (mplus, ++mplus_Length, 0); + simpleAppendDecimalDigitHighPrecision (mminus, ++mminus_Length, 0); + while (RLength > 1 && R[RLength - 1] == 0) + --RLength; + while (mplus_Length > 1 && mplus[mplus_Length - 1] == 0) + --mplus_Length; + while (mminus_Length > 1 && mminus[mminus_Length - 1] == 0) + --mminus_Length; + Kotlin_IntArray_set(uArray, setCount++, U); + //uArray[setCount++] = U; + } + while (1); + + simpleShiftLeftHighPrecision (R, ++RLength, 1); + if (low && !high) + Kotlin_IntArray_set(uArray, setCount++, U); + //uArray[setCount++] = U; + else if (high && !low) + Kotlin_IntArray_set(uArray, setCount++, U + 1); + //uArray[setCount++] = U + 1; + else if (compareHighPrecision (R, RLength, S, SLength) < 0) + Kotlin_IntArray_set(uArray, setCount++, U); + //uArray[setCount++] = U; + else + Kotlin_IntArray_set(uArray, setCount++, U + 1); + //uArray[setCount++] = U + 1; + + Kotlin_IntArray_set(results, 0, setCount); +// fid = (*env)->GetFieldID (env, clazz, "setCount", "I"); +// (*env)->SetIntField (env, inst, fid, setCount); + + Kotlin_IntArray_set(results, 1, getCount); +// fid = (*env)->GetFieldID (env, clazz, "getCount", "I"); +// (*env)->SetIntField (env, inst, fid, getCount); + + Kotlin_IntArray_set(results, 2, firstK); +// fid = (*env)->GetFieldID (env, clazz, "firstK", "I"); +// (*env)->SetIntField (env, inst, fid, firstK); + +} diff --git a/runtime/src/main/cpp/dtoa/fltconst.h b/runtime/src/main/cpp/dtoa/fltconst.h new file mode 100644 index 00000000000..6ab4c389191 --- /dev/null +++ b/runtime/src/main/cpp/dtoa/fltconst.h @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(fltconst_h) +#define fltconst_h + +#include "hycomp.h" +/* IEEE floats consist of: sign bit, exponent field, significand field + single: 31 = sign bit, 30..23 = exponent (8 bits), 22..0 = significand (23 bits) + double: 63 = sign bit, 62..52 = exponent (11 bits), 51..0 = significand (52 bits) + inf == (all exponent bits set) and (all mantissa bits clear) + nan == (all exponent bits set) and (at least one mantissa bit set) + finite == (at least one exponent bit clear) + zero == (all exponent bits clear) and (all mantissa bits clear) + denormal == (all exponent bits clear) and (at least one mantissa bit set) + positive == sign bit clear + negative == sign bit set +*/ +#define MAX_U32_DOUBLE (ESDOUBLE) (4294967296.0) /* 2^32 */ +#define MAX_U32_SINGLE (ESSINGLE) (4294967296.0) /* 2^32 */ +#define HY_POS_PI (ESDOUBLE)(3.141592653589793) + +#ifdef HY_LITTLE_ENDIAN +#ifdef HY_PLATFORM_DOUBLE_ORDER +#define DOUBLE_LO_OFFSET 0 +#define DOUBLE_HI_OFFSET 1 +#else +#define DOUBLE_LO_OFFSET 1 +#define DOUBLE_HI_OFFSET 0 +#endif +#define LONG_LO_OFFSET 0 +#define LONG_HI_OFFSET 1 +#else +#ifdef HY_PLATFORM_DOUBLE_ORDER +#define DOUBLE_LO_OFFSET 1 +#define DOUBLE_HI_OFFSET 0 +#else +#define DOUBLE_LO_OFFSET 0 +#define DOUBLE_HI_OFFSET 1 +#endif +#define LONG_LO_OFFSET 1 +#define LONG_HI_OFFSET 0 +#endif + +#define RETURN_FINITE 0 +#define RETURN_NAN 1 +#define RETURN_POS_INF 2 +#define RETURN_NEG_INF 3 +#define DOUBLE_SIGN_MASK_HI 0x80000000 +#define DOUBLE_EXPONENT_MASK_HI 0x7FF00000 +#define DOUBLE_MANTISSA_MASK_LO 0xFFFFFFFF +#define DOUBLE_MANTISSA_MASK_HI 0x000FFFFF +#define SINGLE_SIGN_MASK 0x80000000 +#define SINGLE_EXPONENT_MASK 0x7F800000 +#define SINGLE_MANTISSA_MASK 0x007FFFFF +#define SINGLE_NAN_BITS (SINGLE_EXPONENT_MASK | 0x00400000) +typedef union u64u32dbl_tag { + U_64 u64val; + U_32 u32val[2]; + I_32 i32val[2]; + double dval; +} U64U32DBL; +/* Replace P_FLOAT_HI and P_FLOAT_LOW */ +/* These macros are used to access the high and low 32-bit parts of a double (64-bit) value. */ +#define LOW_U32_FROM_DBL_PTR(dblptr) (((U64U32DBL *)(dblptr))->u32val[DOUBLE_LO_OFFSET]) +#define HIGH_U32_FROM_DBL_PTR(dblptr) (((U64U32DBL *)(dblptr))->u32val[DOUBLE_HI_OFFSET]) +#define LOW_I32_FROM_DBL_PTR(dblptr) (((U64U32DBL *)(dblptr))->i32val[DOUBLE_LO_OFFSET]) +#define HIGH_I32_FROM_DBL_PTR(dblptr) (((U64U32DBL *)(dblptr))->i32val[DOUBLE_HI_OFFSET]) +#define LOW_U32_FROM_DBL(dbl) LOW_U32_FROM_DBL_PTR(&(dbl)) +#define HIGH_U32_FROM_DBL(dbl) HIGH_U32_FROM_DBL_PTR(&(dbl)) +#define LOW_I32_FROM_DBL(dbl) LOW_I32_FROM_DBL_PTR(&(dbl)) +#define HIGH_I32_FROM_DBL(dbl) HIGH_I32_FROM_DBL_PTR(&(dbl)) +#define LOW_U32_FROM_LONG64_PTR(long64ptr) (((U64U32DBL *)(long64ptr))->u32val[LONG_LO_OFFSET]) +#define HIGH_U32_FROM_LONG64_PTR(long64ptr) (((U64U32DBL *)(long64ptr))->u32val[LONG_HI_OFFSET]) +#define LOW_I32_FROM_LONG64_PTR(long64ptr) (((U64U32DBL *)(long64ptr))->i32val[LONG_LO_OFFSET]) +#define HIGH_I32_FROM_LONG64_PTR(long64ptr) (((U64U32DBL *)(long64ptr))->i32val[LONG_HI_OFFSET]) +#define LOW_U32_FROM_LONG64(long64) LOW_U32_FROM_LONG64_PTR(&(long64)) +#define HIGH_U32_FROM_LONG64(long64) HIGH_U32_FROM_LONG64_PTR(&(long64)) +#define LOW_I32_FROM_LONG64(long64) LOW_I32_FROM_LONG64_PTR(&(long64)) +#define HIGH_I32_FROM_LONG64(long64) HIGH_I32_FROM_LONG64_PTR(&(long64)) +#define IS_ZERO_DBL_PTR(dblptr) ((LOW_U32_FROM_DBL_PTR(dblptr) == 0) && ((HIGH_U32_FROM_DBL_PTR(dblptr) == 0) || (HIGH_U32_FROM_DBL_PTR(dblptr) == DOUBLE_SIGN_MASK_HI))) +#define IS_ONE_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) == 0x3ff00000 || HIGH_U32_FROM_DBL_PTR(dblptr) == 0xbff00000) && (LOW_U32_FROM_DBL_PTR(dblptr) == 0)) +#define IS_NAN_DBL_PTR(dblptr) (((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_EXPONENT_MASK_HI) == DOUBLE_EXPONENT_MASK_HI) && (LOW_U32_FROM_DBL_PTR(dblptr) | (HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_MANTISSA_MASK_HI))) +#define IS_INF_DBL_PTR(dblptr) (((HIGH_U32_FROM_DBL_PTR(dblptr) & (DOUBLE_EXPONENT_MASK_HI|DOUBLE_MANTISSA_MASK_HI)) == DOUBLE_EXPONENT_MASK_HI) && (LOW_U32_FROM_DBL_PTR(dblptr) == 0)) +#define IS_DENORMAL_DBL_PTR(dblptr) (((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_EXPONENT_MASK_HI) == 0) && ((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_MANTISSA_MASK_HI) != 0 || (LOW_U32_FROM_DBL_PTR(dblptr) != 0))) +#define IS_FINITE_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_EXPONENT_MASK_HI) < DOUBLE_EXPONENT_MASK_HI) +#define IS_POSITIVE_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_SIGN_MASK_HI) == 0) +#define IS_NEGATIVE_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) & DOUBLE_SIGN_MASK_HI) != 0) +#define IS_NEGATIVE_MAX_DBL_PTR(dblptr) ((HIGH_U32_FROM_DBL_PTR(dblptr) == 0xFFEFFFFF) && (LOW_U32_FROM_DBL_PTR(dblptr) == 0xFFFFFFFF)) +#define IS_ZERO_DBL(dbl) IS_ZERO_DBL_PTR(&(dbl)) +#define IS_ONE_DBL(dbl) IS_ONE_DBL_PTR(&(dbl)) +#define IS_NAN_DBL(dbl) IS_NAN_DBL_PTR(&(dbl)) +#define IS_INF_DBL(dbl) IS_INF_DBL_PTR(&(dbl)) +#define IS_DENORMAL_DBL(dbl) IS_DENORMAL_DBL_PTR(&(dbl)) +#define IS_FINITE_DBL(dbl) IS_FINITE_DBL_PTR(&(dbl)) +#define IS_POSITIVE_DBL(dbl) IS_POSITIVE_DBL_PTR(&(dbl)) +#define IS_NEGATIVE_DBL(dbl) IS_NEGATIVE_DBL_PTR(&(dbl)) +#define IS_NEGATIVE_MAX_DBL(dbl) IS_NEGATIVE_MAX_DBL_PTR(&(dbl)) +#define IS_ZERO_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK) == (U_32)0) +#define IS_ONE_SNGL_PTR(fltptr) ((*U32P((fltptr)) == 0x3f800000) || (*U32P((fltptr)) == 0xbf800000)) +#define IS_NAN_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK) > (U_32)SINGLE_EXPONENT_MASK) +#define IS_INF_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK) == (U_32)SINGLE_EXPONENT_MASK) +#define IS_DENORMAL_SNGL_PTR(fltptr) (((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK)-(U_32)1) < (U_32)SINGLE_MANTISSA_MASK) +#define IS_FINITE_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)~SINGLE_SIGN_MASK) < (U_32)SINGLE_EXPONENT_MASK) +#define IS_POSITIVE_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)SINGLE_SIGN_MASK) == (U_32)0) +#define IS_NEGATIVE_SNGL_PTR(fltptr) ((*U32P((fltptr)) & (U_32)SINGLE_SIGN_MASK) != (U_32)0) +#define IS_ZERO_SNGL(flt) IS_ZERO_SNGL_PTR(&(flt)) +#define IS_ONE_SNGL(flt) IS_ONE_SNGL_PTR(&(flt)) +#define IS_NAN_SNGL(flt) IS_NAN_SNGL_PTR(&(flt)) +#define IS_INF_SNGL(flt) IS_INF_SNGL_PTR(&(flt)) +#define IS_DENORMAL_SNGL(flt) IS_DENORMAL_SNGL_PTR(&(flt)) +#define IS_FINITE_SNGL(flt) IS_FINITE_SNGL_PTR(&(flt)) +#define IS_POSITIVE_SNGL(flt) IS_POSITIVE_SNGL_PTR(&(flt)) +#define IS_NEGATIVE_SNGL(flt) IS_NEGATIVE_SNGL_PTR(&(flt)) +#define SET_NAN_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = (DOUBLE_EXPONENT_MASK_HI | 0x00080000); LOW_U32_FROM_DBL_PTR(dblptr) = 0 +#define SET_PZERO_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = 0; LOW_U32_FROM_DBL_PTR(dblptr) = 0 +#define SET_NZERO_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = DOUBLE_SIGN_MASK_HI; LOW_U32_FROM_DBL_PTR(dblptr) = 0 +#define SET_PINF_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = DOUBLE_EXPONENT_MASK_HI; LOW_U32_FROM_DBL_PTR(dblptr) = 0 +#define SET_NINF_DBL_PTR(dblptr) HIGH_U32_FROM_DBL_PTR(dblptr) = (DOUBLE_EXPONENT_MASK_HI | DOUBLE_SIGN_MASK_HI); LOW_U32_FROM_DBL_PTR(dblptr) = 0 +#define SET_NAN_SNGL_PTR(fltptr) *U32P((fltptr)) = ((U_32)SINGLE_NAN_BITS) +#define SET_PZERO_SNGL_PTR(fltptr) *U32P((fltptr)) = 0 +#define SET_NZERO_SNGL_PTR(fltptr) *U32P((fltptr)) = SINGLE_SIGN_MASK +#define SET_PINF_SNGL_PTR(fltptr) *U32P((fltptr)) = SINGLE_EXPONENT_MASK +#define SET_NINF_SNGL_PTR(fltptr) *U32P((fltptr)) = (SINGLE_EXPONENT_MASK | SINGLE_SIGN_MASK) + +#if defined(HY_WORD64) + #define PTR_DOUBLE_VALUE(dstPtr, aDoublePtr) ((U64U32DBL *)(aDoublePtr))->u64val = ((U64U32DBL *)(dstPtr))->u64val + #define PTR_DOUBLE_STORE(dstPtr, aDoublePtr) ((U64U32DBL *)(dstPtr))->u64val = ((U64U32DBL *)(aDoublePtr))->u64val + #define STORE_LONG(dstPtr, hi, lo) ((U64U32DBL *)(dstPtr))->u64val = (((U_64)(hi)) << 32) | (lo) +#else + /* on some platforms (HP720) we cannot reference an unaligned float. Build them by hand, one U_32 at a time. */ + #if defined(ATOMIC_FLOAT_ACCESS) + #define PTR_DOUBLE_STORE(dstPtr, aDoublePtr) HIGH_U32_FROM_DBL_PTR(dstPtr) = HIGH_U32_FROM_DBL_PTR(aDoublePtr); LOW_U32_FROM_DBL_PTR(dstPtr) = LOW_U32_FROM_DBL_PTR(aDoublePtr) + #define PTR_DOUBLE_VALUE(dstPtr, aDoublePtr) HIGH_U32_FROM_DBL_PTR(aDoublePtr) = HIGH_U32_FROM_DBL_PTR(dstPtr); LOW_U32_FROM_DBL_PTR(aDoublePtr) = LOW_U32_FROM_DBL_PTR(dstPtr) + #else + #define PTR_DOUBLE_STORE(dstPtr, aDoublePtr) (*(dstPtr) = *(aDoublePtr)) + #define PTR_DOUBLE_VALUE(dstPtr, aDoublePtr) (*(aDoublePtr) = *(dstPtr)) + #endif + + #define STORE_LONG(dstPtr, hi, lo) HIGH_U32_FROM_LONG64_PTR(dstPtr) = (hi); LOW_U32_FROM_LONG64_PTR(dstPtr) = (lo) +#endif /* HY_WORD64 */ + +#define PTR_SINGLE_VALUE(dstPtr, aSinglePtr) (*U32P(aSinglePtr) = *U32P(dstPtr)) +#define PTR_SINGLE_STORE(dstPtr, aSinglePtr) *((U_32 *)(dstPtr)) = (*U32P(aSinglePtr)) + +#endif /* fltconst_h */ diff --git a/runtime/src/main/cpp/dtoa/fltparse.cpp b/runtime/src/main/cpp/dtoa/fltparse.cpp new file mode 100644 index 00000000000..df9137aca41 --- /dev/null +++ b/runtime/src/main/cpp/dtoa/fltparse.cpp @@ -0,0 +1,559 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "cbigint.h" +#include "../Natives.h" +#include "../Exceptions.h" +#include "../utf8.h" +#include +#include + +#if defined(LINUX) || defined(FREEBSD) || defined(MACOSX) || defined(ZOS) || defined(AIX) +#define USE_LL +#endif + +#ifdef HY_LITTLE_ENDIAN +#define LOW_I32_FROM_PTR(ptr64) (*(I_32 *) (ptr64)) +#else +#define LOW_I32_FROM_PTR(ptr64) (*(((I_32 *) (ptr64)) + 1)) +#endif + +#define MAX_ACCURACY_WIDTH 8 + +#define DEFAULT_WIDTH MAX_ACCURACY_WIDTH + +extern "C" { +KFloat +Konan_FloatingPointParser_parseFloatImpl (KString s, KInt e); +} + +KFloat createFloat1 (U_64 * f, IDATA length, KInt e); +KFloat floatAlgorithm (U_64 * f, IDATA length, KInt e, KFloat z); +KFloat createFloat (const char *s, KInt e); + +static const U_32 tens[] = { + 0x3f800000, + 0x41200000, + 0x42c80000, + 0x447a0000, + 0x461c4000, + 0x47c35000, + 0x49742400, + 0x4b189680, + 0x4cbebc20, + 0x4e6e6b28, + 0x501502f9 /* 10 ^ 10 in float */ +}; + +#define tenToTheE(e) (*((KFloat *) (tens + (e)))) +#define LOG5_OF_TWO_TO_THE_N 11 + +#define sizeOfTenToTheE(e) (((e) / 19) + 1) + +#define INFINITE_INTBITS (0x7F800000) +#define MINIMUM_INTBITS (1) + +#define MANTISSA_MASK (0x007FFFFF) +#define EXPONENT_MASK (0x7F800000) +#define NORMAL_MASK (0x00800000) +#define FLOAT_TO_INTBITS(flt) (*((U_32 *)(&flt))) + +/* Keep a count of the number of times we decrement and increment to + * approximate the double, and attempt to detect the case where we + * could potentially toggle back and forth between decrementing and + * incrementing. It is possible for us to be stuck in the loop when + * incrementing by one or decrementing by one may exceed or stay below + * the value that we are looking for. In this case, just break out of + * the loop if we toggle between incrementing and decrementing for more + * than twice. + */ +#define INCREMENT_FLOAT(_x, _decCount, _incCount) \ + { \ + ++FLOAT_TO_INTBITS(_x); \ + _incCount++; \ + if( (_incCount > 2) && (_decCount > 2) ) { \ + if( _decCount > _incCount ) { \ + FLOAT_TO_INTBITS(_x) += _decCount - _incCount; \ + } else if( _incCount > _decCount ) { \ + FLOAT_TO_INTBITS(_x) -= _incCount - _decCount; \ + } \ + break; \ + } \ + } +#define DECREMENT_FLOAT(_x, _decCount, _incCount) \ + { \ + --FLOAT_TO_INTBITS(_x); \ + _decCount++; \ + if( (_incCount > 2) && (_decCount > 2) ) { \ + if( _decCount > _incCount ) { \ + FLOAT_TO_INTBITS(_x) += _decCount - _incCount; \ + } else if( _incCount > _decCount ) { \ + FLOAT_TO_INTBITS(_x) -= _incCount - _decCount; \ + } \ + break; \ + } \ + } + +#define allocateU64(x, n) if (!((x) = (U_64*) malloc((n) * sizeof(U_64)))) goto OutOfMemory; +#define release(r) if ((r)) free((r)); + +KFloat +createFloat (const char *s, KInt e) +{ + /* assumes s is a null terminated string with at least one + * character in it */ + U_64 def[DEFAULT_WIDTH]; + U_64 defBackup[DEFAULT_WIDTH]; + U_64 *f, *fNoOverflow, *g, *tempBackup; + U_32 overflow; + KFloat result; + IDATA index = 1; + int unprocessedDigits = 0; + + f = def; + fNoOverflow = defBackup; + *f = 0; + tempBackup = g = 0; + do + { + if (*s >= '0' && *s <= '9') + { + /* Make a back up of f before appending, so that we can + * back out of it if there is no more room, i.e. index > + * MAX_ACCURACY_WIDTH. + */ + memcpy (fNoOverflow, f, sizeof (U_64) * index); + overflow = + simpleAppendDecimalDigitHighPrecision (f, index, *s - '0'); + if (overflow) + { + + f[index++] = overflow; + /* There is an overflow, but there is no more room + * to store the result. We really only need the top 52 + * bits anyway, so we must back out of the overflow, + * and ignore the rest of the string. + */ + if (index >= MAX_ACCURACY_WIDTH) + { + index--; + memcpy (f, fNoOverflow, sizeof (U_64) * index); + break; + } + if (tempBackup) + { + fNoOverflow = tempBackup; + } + } + } + else + index = -1; + } + while (index > 0 && *(++s) != '\0'); + + /* We've broken out of the parse loop either because we've reached + * the end of the string or we've overflowed the maximum accuracy + * limit of a double. If we still have unprocessed digits in the + * given string, then there are three possible results: + * 1. (unprocessed digits + e) == 0, in which case we simply + * convert the existing bits that are already parsed + * 2. (unprocessed digits + e) < 0, in which case we simply + * convert the existing bits that are already parsed along + * with the given e + * 3. (unprocessed digits + e) > 0 indicates that the value is + * simply too big to be stored as a double, so return Infinity + */ + if ((unprocessedDigits = strlen (s)) > 0) + { + e += unprocessedDigits; + if (index > -1) + { + if (e <= 0) + { + result = createFloat1 (f, index, e); + } + else + { + FLOAT_TO_INTBITS (result) = INFINITE_INTBITS; + } + } + else + { + result = *(KFloat *) & index; + } + } + else + { + if (index > -1) + { + result = createFloat1 (f, index, e); + } + else + { + result = *(KFloat *) & index; + } + } + + return result; + +} + +KFloat +createFloat1 (U_64 * f, IDATA length, KInt e) +{ + IDATA numBits; + KDouble dresult; + KFloat result; + + numBits = highestSetBitHighPrecision (f, length) + 1; + if (numBits < 25 && e >= 0 && e < LOG5_OF_TWO_TO_THE_N) + { + return ((KFloat) LOW_I32_FROM_PTR (f)) * tenToTheE (e); + } + else if (numBits < 25 && e < 0 && (-e) < LOG5_OF_TWO_TO_THE_N) + { + return ((KFloat) LOW_I32_FROM_PTR (f)) / tenToTheE (-e); + } + else if (e >= 0 && e < 39) + { + result = (KFloat) (toDoubleHighPrecision (f, length) * pow (10.0, (double) e)); + } + else if (e >= 39) + { + /* Convert the partial result to make sure that the + * non-exponential part is not zero. This check fixes the case + * where the user enters 0.0e309! */ + result = (KFloat) toDoubleHighPrecision (f, length); + + if (result == 0.0) + + FLOAT_TO_INTBITS (result) = MINIMUM_INTBITS; + else + FLOAT_TO_INTBITS (result) = INFINITE_INTBITS; + } + else if (e > -309) + { + int dexp; + U_32 fmant, fovfl; + U_64 dmant; + dresult = toDoubleHighPrecision (f, length) / pow (10.0, (double) -e); + if (IS_DENORMAL_DBL (dresult)) + { + FLOAT_TO_INTBITS (result) = 0; + return result; + } + dexp = doubleExponent (dresult) + 51; + dmant = doubleMantissa (dresult); + /* Is it too small to be represented by a single-precision + * float? */ + if (dexp <= -155) + { + FLOAT_TO_INTBITS (result) = 0; + return result; + } + /* Is it a denormalized single-precision float? */ + if ((dexp <= -127) && (dexp > -155)) + { + /* Only interested in 24 msb bits of the 53-bit double mantissa */ + fmant = (U_32) (dmant >> 29); + fovfl = ((U_32) (dmant & 0x1FFFFFFF)) << 3; + while ((dexp < -127) && ((fmant | fovfl) != 0)) + { + if ((fmant & 1) != 0) + { + fovfl |= 0x80000000; + } + fovfl >>= 1; + fmant >>= 1; + dexp++; + } + if ((fovfl & 0x80000000) != 0) + { + if ((fovfl & 0x7FFFFFFC) != 0) + { + fmant++; + } + else if ((fmant & 1) != 0) + { + fmant++; + } + } + else if ((fovfl & 0x40000000) != 0) + { + if ((fovfl & 0x3FFFFFFC) != 0) + { + fmant++; + } + } + FLOAT_TO_INTBITS (result) = fmant; + } + else + { + result = (KFloat) dresult; + } + } + + /* Don't go straight to zero as the fact that x*0 = 0 independent + * of x might cause the algorithm to produce an incorrect result. + * Instead try the min value first and let it fall to zero if need + * be. + */ + if (e <= -309 || FLOAT_TO_INTBITS (result) == 0) + FLOAT_TO_INTBITS (result) = MINIMUM_INTBITS; + + return floatAlgorithm (f, length, e, (KFloat) result); +} + +#if defined(WIN32) +/* disable global optimizations on the microsoft compiler for the + * floatAlgorithm function otherwise it won't properly compile */ +#pragma optimize("g",off) +#endif + +/* The algorithm for the function floatAlgorithm() below can be found + * in: + * + * "How to Read Floating-Point Numbers Accurately", William D. + * Clinger, Proceedings of the ACM SIGPLAN '90 Conference on + * Programming Language Design and Implementation, June 20-22, + * 1990, pp. 92-101. + * + * There is a possibility that the function will end up in an endless + * loop if the given approximating floating-point number (a very small + * floating-point whose value is very close to zero) straddles between + * two approximating integer values. We modified the algorithm slightly + * to detect the case where it oscillates back and forth between + * incrementing and decrementing the floating-point approximation. It + * is currently set such that if the oscillation occurs more than twice + * then return the original approximation. + */ +KFloat +floatAlgorithm (U_64 * f, IDATA length, KInt e, KFloat z) +{ + U_64 m; + IDATA k, comparison, comparison2; + U_64 *x, *y, *D, *D2; + IDATA xLength, yLength, DLength, D2Length; + IDATA decApproxCount, incApproxCount; + //PORT_ACCESS_FROM_ENV (env); + + x = y = D = D2 = 0; + xLength = yLength = DLength = D2Length = 0; + decApproxCount = incApproxCount = 0; + + do + { + m = floatMantissa (z); + k = floatExponent (z); + + if (x && x != f) + //jclmem_free_memory (env, x); + release(x); + release (y); + release (D); + release (D2); + + if (e >= 0 && k >= 0) + { + xLength = sizeOfTenToTheE (e) + length; + allocateU64 (x, xLength); + memset (x + length, 0, sizeof (U_64) * (xLength - length)); + memcpy (x, f, sizeof (U_64) * length); + timesTenToTheEHighPrecision (x, xLength, e); + + yLength = (k >> 6) + 2; + allocateU64 (y, yLength); + memset (y + 1, 0, sizeof (U_64) * (yLength - 1)); + *y = m; + simpleShiftLeftHighPrecision (y, yLength, k); + } + else if (e >= 0) + { + xLength = sizeOfTenToTheE (e) + length + ((-k) >> 6) + 1; + allocateU64 (x, xLength); + memset (x + length, 0, sizeof (U_64) * (xLength - length)); + memcpy (x, f, sizeof (U_64) * length); + timesTenToTheEHighPrecision (x, xLength, e); + simpleShiftLeftHighPrecision (x, xLength, -k); + + yLength = 1; + allocateU64 (y, 1); + *y = m; + } + else if (k >= 0) + { + xLength = length; + x = f; + + yLength = sizeOfTenToTheE (-e) + 2 + (k >> 6); + allocateU64 (y, yLength); + memset (y + 1, 0, sizeof (U_64) * (yLength - 1)); + *y = m; + timesTenToTheEHighPrecision (y, yLength, -e); + simpleShiftLeftHighPrecision (y, yLength, k); + } + else + { + xLength = length + ((-k) >> 6) + 1; + allocateU64 (x, xLength); + memset (x + length, 0, sizeof (U_64) * (xLength - length)); + memcpy (x, f, sizeof (U_64) * length); + simpleShiftLeftHighPrecision (x, xLength, -k); + + yLength = sizeOfTenToTheE (-e) + 1; + allocateU64 (y, yLength); + memset (y + 1, 0, sizeof (U_64) * (yLength - 1)); + *y = m; + timesTenToTheEHighPrecision (y, yLength, -e); + } + + comparison = compareHighPrecision (x, xLength, y, yLength); + if (comparison > 0) + { /* x > y */ + DLength = xLength; + allocateU64 (D, DLength); + memcpy (D, x, DLength * sizeof (U_64)); + subtractHighPrecision (D, DLength, y, yLength); + } + else if (comparison) + { /* y > x */ + DLength = yLength; + allocateU64 (D, DLength); + memcpy (D, y, DLength * sizeof (U_64)); + subtractHighPrecision (D, DLength, x, xLength); + } + else + { /* y == x */ + DLength = 1; + allocateU64 (D, 1); + *D = 0; + } + + D2Length = DLength + 1; + allocateU64 (D2, D2Length); + m <<= 1; + multiplyHighPrecision (D, DLength, &m, 1, D2, D2Length); + m >>= 1; + + comparison2 = compareHighPrecision (D2, D2Length, y, yLength); + if (comparison2 < 0) + { + if (comparison < 0 && m == NORMAL_MASK) + { + simpleShiftLeftHighPrecision (D2, D2Length, 1); + if (compareHighPrecision (D2, D2Length, y, yLength) > 0) + { + DECREMENT_FLOAT (z, decApproxCount, incApproxCount); + } + else + { + break; + } + } + else + { + break; + } + } + else if (comparison2 == 0) + { + if ((m & 1) == 0) + { + if (comparison < 0 && m == NORMAL_MASK) + { + DECREMENT_FLOAT (z, decApproxCount, incApproxCount); + } + else + { + break; + } + } + else if (comparison < 0) + { + DECREMENT_FLOAT (z, decApproxCount, incApproxCount); + break; + } + else + { + INCREMENT_FLOAT (z, decApproxCount, incApproxCount); + break; + } + } + else if (comparison < 0) + { + DECREMENT_FLOAT (z, decApproxCount, incApproxCount); + } + else + { + if (FLOAT_TO_INTBITS (z) == EXPONENT_MASK) + break; + INCREMENT_FLOAT (z, decApproxCount, incApproxCount); + } + } + while (1); + + if (x && x != f) + //jclmem_free_memory (env, x); + release(x); + release (y); + release (D); + release (D2); + return z; + +OutOfMemory: + if (x && x != f) + //jclmem_free_memory (env, x); + release(x); + release (y); + release (D); + release (D2); + + FLOAT_TO_INTBITS (z) = -2; + + return z; +} + +#if defined(WIN32) +#pragma optimize("",on) /*restore optimizations */ +#endif + +KFloat +Konan_FloatingPointParser_parseFloatImpl (KString s, KInt e) +{ + const KChar* utf16 = CharArrayAddressOfElementAt(s, 0); + std::string utf8; + utf8::utf16to8(utf16, utf16 + s->count_, back_inserter(utf8)); + const char *str = utf8.c_str(); + auto flt = createFloat (str, e); + + if (((I_32) FLOAT_TO_INTBITS (flt)) >= 0) + { + return flt; + } + else if (((I_32) FLOAT_TO_INTBITS (flt)) == (I_32) - 1) + { /* NumberFormatException */ + ThrowNumberFormatException(); + } + else + { /* OutOfMemoryError */ + ThrowOutOfMemoryError(); + } + + return 0.0; +} diff --git a/runtime/src/main/cpp/dtoa/hycomp.h b/runtime/src/main/cpp/dtoa/hycomp.h new file mode 100644 index 00000000000..994b49ac27a --- /dev/null +++ b/runtime/src/main/cpp/dtoa/hycomp.h @@ -0,0 +1,523 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(hycomp_h) +#define hycomp_h +// TODO: Move to settings +#define MACOSX +/** + * USE_PROTOTYPES: Use full ANSI prototypes. + * + * CLOCK_PRIMS: We want the timer/clock prims to be used + * + * LITTLE_ENDIAN: This is for the intel machines or other + * little endian processors. Defaults to big endian. + * + * NO_LVALUE_CASTING: This is for compilers that don't like the left side + * of assigns to be cast. It hacks around to do the + * right thing. + * + * ATOMIC_FLOAT_ACCESS: So that float operations will work. + * + * LINKED_USER_PRIMITIVES: Indicates that user primitives are statically linked + * with the VM executeable. + * + * OLD_SPACE_SIZE_DIFF: The 68k uses a different amount of old space. + * This "legitimizes" the change. + * + * SIMPLE_SIGNAL: For machines that don't use real signals in C. + * (eg: PC, 68k) + * + * OS_NAME_LOOKUP: Use nlist to lookup user primitive addresses. + * + * VMCALL: Tag for all functions called by the VM. + * + * VMAPICALL: Tag for all functions called via the PlatformFunction + * callWith: mechanism. + * + * SYS_FLOAT: For some math functions where extended types (80 or 96 bits) are returned + * Most platforms return as a double + * + * FLOAT_EXTENDED: If defined, the type name for extended precision floats. + * + * PLATFORM_IS_ASCII: Must be defined if the platform is ASCII + * + * EXE_EXTENSION_CHAR: the executable has a delimiter that we want to stop at as part of argv[0]. + */ + + /** + * By default order doubles in the native (that is big/little endian) ordering. + */ + +#define HY_PLATFORM_DOUBLE_ORDER + +/** + * Define common types: + *
    + *
  • U_32 / I_32 - unsigned/signed 32 bits
  • + *
  • U_16 / I_16 - unsigned/signed 16 bits
  • + *
  • U_8 / I_8 - unsigned/signed 8 bits (bytes -- not to be + * confused with char)
  • + *
+ */ + +typedef int I_32; +typedef short I_16; +typedef signed char I_8; /* chars can be unsigned */ +typedef unsigned int U_32; +typedef unsigned short U_16; +typedef unsigned char U_8; + +/** + * Define platform specific types: + *
    + *
  • U_64 / I_64 - unsigned/signed 64 bits
  • + *
+ */ + +#if defined(LINUX) || defined(FREEBSD) || defined(AIX) || defined(MACOSX) + +#define DATA_TYPES_DEFINED + +/* NOTE: Linux supports different processors -- do not assume 386 */ +#if defined(HYX86_64) || defined(HYIA64) || defined(HYPPC64) || defined(HYS390X) + +typedef unsigned long int U_64; /* 64bits */ +typedef long int I_64; +#define TOC_UNWRAP_ADDRESS(wrappedPointer) ((void *) (wrappedPointer)[0]) +#define TOC_STORE_TOC(dest,wrappedPointer) (dest = ((UDATA*)wrappedPointer)[1]) + +#define HY_WORD64 + +#else + +typedef long long I_64; +typedef unsigned long long U_64; + +#endif + +#if defined(HYS390X) || defined(HYS390) || defined(HYPPC64) || defined(HYPPC32) +#define HY_BIG_ENDIAN +#else +#define HY_LITTLE_ENDIAN +#endif + +#if defined(HYPPC32) && defined(LINUX) +#define VA_PTR(valist) (&valist[0]) +#endif + +typedef double SYS_FLOAT; +#define HYCONST64(x) x##LL +#define NO_LVALUE_CASTING +#define FLOAT_EXTENDED long double +#define PLATFORM_IS_ASCII +#define PLATFORM_LINE_DELIMITER "\012" +#define DIR_SEPARATOR '/' +#define DIR_SEPARATOR_STR "/" +#define PATH_SEPARATOR ':' +#define PATH_SEPARATOR_STR ":" +#if defined(AIX) +#define LIBPATH_ENV_VAR "LIBPATH" +#else +#if defined(MACOSX) +#define LIBPATH_ENV_VAR "DYLD_LIBRARY_PATH" +#else +#define LIBPATH_ENV_VAR "LD_LIBRARY_PATH" +#endif +#endif +#if defined(MACOSX) +#define PLATFORM_DLL_EXTENSION ".dylib" +#else +#define PLATFORM_DLL_EXTENSION ".so" +#endif + +/** + * No priorities on Linux + */ + +#define HY_PRIORITY_MAP {0,0,0,0,0,0,0,0,0,0,0,0} + +typedef U_32 BOOLEAN; + +#endif + +/* Win32 - Windows 3.1 & NT using Win32 */ +#if defined(WIN32) + +#define HY_LITTLE_ENDIAN + +/* Define 64-bit integers for Windows */ +typedef __int64 I_64; +typedef unsigned __int64 U_64; + +typedef double SYS_FLOAT; +#define NO_LVALUE_CASTING +#define VMAPICALL _stdcall +#define VMCALL _cdecl +#define EXE_EXTENSION_CHAR '.' + +#define DIR_SEPARATOR '\\' +#define DIR_SEPARATOR_STR "\\" +#define PATH_SEPARATOR ';' +#define PATH_SEPARATOR_STR ";" +#define LIBPATH_ENV_VAR "PATH" + +/* Modifications for the Alpha running WIN-NT */ +#if defined(_ALPHA_) +#undef small /* defined as char in rpcndr.h */ +typedef double FLOAT_EXTENDED; +#endif + +#define HY_PRIORITY_MAP { \ + THREAD_PRIORITY_IDLE, /* 0 */\ + THREAD_PRIORITY_LOWEST, /* 1 */\ + THREAD_PRIORITY_BELOW_NORMAL, /* 2 */\ + THREAD_PRIORITY_BELOW_NORMAL, /* 3 */\ + THREAD_PRIORITY_BELOW_NORMAL, /* 4 */\ + THREAD_PRIORITY_NORMAL, /* 5 */\ + THREAD_PRIORITY_ABOVE_NORMAL, /* 6 */\ + THREAD_PRIORITY_ABOVE_NORMAL, /* 7 */\ + THREAD_PRIORITY_ABOVE_NORMAL, /* 8 */\ + THREAD_PRIORITY_ABOVE_NORMAL, /* 9 */\ + THREAD_PRIORITY_HIGHEST, /*10 */\ + THREAD_PRIORITY_TIME_CRITICAL /*11 */} + +#endif /* defined(WIN32) */ + +#if defined(ZOS) + +#define HY_BIG_ENDIAN + +#define DATA_TYPES_DEFINED +typedef unsigned int BOOLEAN; +#if defined (HYS390X) +typedef unsigned long U_64; +typedef long I_64; +#else +typedef signed long long I_64; +typedef unsigned long long U_64; +#endif + +typedef double SYS_FLOAT; + +#define HYCONST64(x) x##LL + +#define NO_LVALUE_CASTING +#define PLATFORM_LINE_DELIMITER "\012" +#define DIR_SEPARATOR '/' +#define DIR_SEPARATOR_STR "/" +#define PATH_SEPARATOR ':' +#define PATH_SEPARATOR_STR ":" +#define LIBPATH_ENV_VAR "LIBPATH" + +#define VA_PTR(valist) (&valist[0]) + +typedef struct { +#if !defined(HYS390X) + char stuff[16]; +#endif + char *ada; + void (*rawFnAddress)(); +} HyFunctionDescriptor_T; + +#define TOC_UNWRAP_ADDRESS(wrappedPointer) (((HyFunctionDescriptor_T *) (wrappedPointer))->rawFnAddress) + +#define PLATFORM_DLL_EXTENSION ".so" + +#ifdef HYS390X +#ifndef HY_WORD64 +#define HY_WORD64 +#endif /* ifndef HY_WORD64 */ +#endif /* HYS390X */ + +#endif /* ZOS */ + + +#if !defined(VMCALL) +#define VMCALL +#define VMAPICALL +#endif +#define PVMCALL VMCALL * + +#define GLOBAL_DATA(symbol) ((void*)&(symbol)) +#define GLOBAL_TABLE(symbol) GLOBAL_DATA(symbol) + +/** + * Define platform specific types: + *
    + *
  • UDATA - unsigned data, can be used as an integer or + * pointer storage
  • + *
  • IDATA - signed data, can be used as an integer or + * pointer storage
  • + *
+ */ +/* FIXME: POINTER64 */ +#if defined(HYX86_64) || defined(HYIA64) || defined(HYPPC64) || defined(HYS390X) || defined(POINTER64) + +typedef I_64 IDATA; +typedef U_64 UDATA; + +#else /* this is default for non-64bit systems */ + +typedef I_32 IDATA; +typedef U_32 UDATA; + +#endif /* defined(HYX86_64) */ + +#if !defined(DATA_TYPES_DEFINED) +/* no generic U_64 or I_64 */ + +/* don't typedef BOOLEAN since it's already def'ed on Win32 */ +#define BOOLEAN UDATA + +#ifndef HY_BIG_ENDIAN +#define HY_LITTLE_ENDIAN +#endif + +#endif + +#if !defined(HYCONST64) +#define HYCONST64(x) x##L +#endif + +#if !defined(HY_DEFAULT_SCHED) + +/** + * By default, pthreads platforms use the SCHED_OTHER thread + * scheduling policy. + */ + +#define HY_DEFAULT_SCHED SCHED_OTHER +#endif + +#if !defined(HY_PRIORITY_MAP) + +/** + * If no priority map if provided, priorities will be determined + * algorithmically. + */ + +#endif + +#if !defined(FALSE) +#define FALSE ((BOOLEAN) 0) +#if !defined(TRUE) +#define TRUE ((BOOLEAN) (!FALSE)) +#endif +#endif + +#if !defined(NULL) +#if defined(__cplusplus) +#define NULL (0) +#else +#define NULL ((void *)0) +#endif +#endif +#define USE_PROTOTYPES +#if defined(USE_PROTOTYPES) +#define PROTOTYPE(x) x +#define VARARGS , ... +#else +#define PROTOTYPE(x) () +#define VARARGS +#endif + +/** + * Assign the default line delimiter, if it was not set. + */ + +#if !defined(PLATFORM_LINE_DELIMITER) +#define PLATFORM_LINE_DELIMITER "\015\012" +#endif + +/** + * Set the max path length, if it was not set. + */ + +#if !defined(MAX_IMAGE_PATH_LENGTH) +#define MAX_IMAGE_PATH_LENGTH (2048) +#endif +typedef double ESDOUBLE; +typedef float ESSINGLE; + +/** + * Helpers for U_64s. + */ + +#define CLEAR_U64(u64) (u64 = (U_64)0) +#define LOW_LONG(l) (*((U_32 *) &(l))) +#define HIGH_LONG(l) (*(((U_32 *) &(l)) + 1)) +#define I8(x) ((I_8) (x)) +#define I8P(x) ((I_8 *) (x)) +#define U16(x) ((U_16) (x)) +#define I16(x) ((I_16) (x)) +#define I16P(x) ((I_16 *) (x)) +#define U32(x) ((U_32) (x)) +#define I32(x) ((I_32) (x)) +#define I32P(x) ((I_32 *) (x)) +#define U16P(x) ((U_16 *) (x)) +#define U32P(x) ((U_32 *) (x)) +#define OBJP(x) ((HyObject *) (x)) +#define OBJPP(x) ((HyObject **) (x)) +#define OBJPPP(x) ((HyObject ***) (x)) +#define CLASSP(x) ((Class *) (x)) +#define CLASSPP(x) ((Class **) (x)) +#define BYTEP(x) ((BYTE *) (x)) + +/** + * Test - was conflicting with OS2.h + */ + +#define ESCHAR(x) ((CHARACTER) (x)) +#define FLT(x) ((FLOAT) x) +#define FLTP(x) ((FLOAT *) (x)) +#if defined(NO_LVALUE_CASTING) +#define LI8(x) (*((I_8 *) &(x))) +#define LI8P(x) (*((I_8 **) &(x))) +#define LU16(x) (*((U_16 *) &(x))) +#define LI16(x) (*((I_16 *) &(x))) +#define LU32(x) (*((U_32 *) &(x))) +#define LI32(x) (*((I_32 *) &(x))) +#define LI32P(x) (*((I_32 **) &(x))) +#define LU16P(x) (*((U_16 **) &(x))) +#define LU32P(x) (*((U_32 **) &(x))) +#define LOBJP(x) (*((HyObject **) &(x))) +#define LOBJPP(x) (*((HyObject ***) &(x))) +#define LOBJPPP(x) (*((HyObject ****) &(x)) +#define LCLASSP(x) (*((Class **) &(x))) +#define LBYTEP(x) (*((BYTE **) &(x))) +#define LCHAR(x) (*((CHARACTER) &(x))) +#define LFLT(x) (*((FLOAT) &x)) +#define LFLTP(x) (*((FLOAT *) &(x))) +#else +#define LI8(x) I8((x)) +#define LI8P(x) I8P((x)) +#define LU16(x) U16((x)) +#define LI16(x) I16((x)) +#define LU32(x) U32((x)) +#define LI32(x) I32((x)) +#define LI32P(x) I32P((x)) +#define LU16P(x) U16P((x)) +#define LU32P(x) U32P((x)) +#define LOBJP(x) OBJP((x)) +#define LOBJPP(x) OBJPP((x)) +#define LOBJPPP(x) OBJPPP((x)) +#define LIOBJP(x) IOBJP((x)) +#define LCLASSP(x) CLASSP((x)) +#define LBYTEP(x) BYTEP((x)) +#define LCHAR(x) CHAR((x)) +#define LFLT(x) FLT((x)) +#define LFLTP(x) FLTP((x)) +#endif + +/** + * Macros for converting between words and longs and accessing bits. + */ + +#define HIGH_WORD(x) U16(U32((x)) >> 16) +#define LOW_WORD(x) U16(U32((x)) & 0xFFFF) +#define LOW_BIT(o) (U32((o)) & 1) +#define LOW_2_BITS(o) (U32((o)) & 3) +#define LOW_3_BITS(o) (U32((o)) & 7) +#define LOW_4_BITS(o) (U32((o)) & 15) +#define MAKE_32(h, l) ((U32((h)) << 16) | U32((l))) +#define MAKE_64(h, l) ((((I_64)(h)) << 32) | (l)) +#if defined(__cplusplus) +#define HY_CFUNC "C" +#define HY_CDATA "C" +#else +#define HY_CFUNC +#define HY_CDATA +#endif + +/** + * Macros for tagging functions which read/write the vm thread. + */ + +#define READSVMTHREAD +#define WRITESVMTHREAD +#define REQUIRESSTACKFRAME + +/** + * Macro for tagging functions, which never return. + */ + +#if defined(__GNUC__) + +/** + * On GCC, we can actually pass this information on to the compiler. + */ + +#define NORETURN __attribute__((noreturn)) +#else +#define NORETURN +#endif + +/** + * On some systems va_list is an array type. This is probably in + * violation of the ANSI C spec, but it's not entirely clear. Because of + * this, we end up with an undesired extra level of indirection if we take + * the address of a va_list argument. + * + * To get it right, always use the VA_PTR macro + */ + +#if !defined(VA_PTR) +#define VA_PTR(valist) (&valist) +#endif +#if !defined(TOC_UNWRAP_ADDRESS) +#define TOC_UNWRAP_ADDRESS(wrappedPointer) (wrappedPointer) +#endif + +#if !defined(TOC_STORE_TOC) +#define TOC_STORE_TOC(dest,wrappedPointer) +#endif +/** + * Macros for accessing I_64 values. + */ + +#if defined(ATOMIC_LONG_ACCESS) +#define PTR_LONG_STORE(dstPtr, aLongPtr) ((*U32P(dstPtr) = *U32P(aLongPtr)), (*(U32P(dstPtr)+1) = *(U32P(aLongPtr)+1))) +#define PTR_LONG_VALUE(dstPtr, aLongPtr) ((*U32P(aLongPtr) = *U32P(dstPtr)), (*(U32P(aLongPtr)+1) = *(U32P(dstPtr)+1))) +#else +#define PTR_LONG_STORE(dstPtr, aLongPtr) (*(dstPtr) = *(aLongPtr)) +#define PTR_LONG_VALUE(dstPtr, aLongPtr) (*(aLongPtr) = *(dstPtr)) +#endif + +/** + * Macro used when declaring tables which require relocations. + */ + +#if !defined(HYCONST_TABLE) +#define HYCONST_TABLE const +#endif + +/** + * ANSI qsort is not always available. + */ + +#if !defined(HY_SORT) +#define HY_SORT(base, nmemb, size, compare) qsort((base), (nmemb), (size), (compare)) +#endif + +/** + * Helper macros for storing/restoring pointers to jlong. + */ +#define jlong2addr(a, x) ((a *)((IDATA)(x))) +#define addr2jlong(x) ((jlong)((IDATA)(x))) + +#endif /* hycomp_h */ diff --git a/runtime/src/main/kotlin/konan/internal/FloatingPointParser.kt b/runtime/src/main/kotlin/konan/internal/FloatingPointParser.kt new file mode 100644 index 00000000000..bcb41652f57 --- /dev/null +++ b/runtime/src/main/kotlin/konan/internal/FloatingPointParser.kt @@ -0,0 +1,415 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package konan.internal + +import kotlin.comparisons.* + +/** + * Takes a String and an integer exponent. The String should hold a positive + * integer value (or zero). The exponent will be used to calculate the + * floating point number by taking the positive integer the String + * represents and multiplying by 10 raised to the power of the + * exponent. Returns the closest double value to the real number. + + * @param s + * * the String that will be parsed to a floating point + * * + * @param e + * * an int represent the 10 to part + * * + * @return the double closest to the real number + * * + * * + * @exception NumberFormatException + * * if the String doesn't represent a positive integer value + */ +@SymbolName("Konan_FloatingPointParser_parseDoubleImpl") +private external fun parseDoubleImpl(s: String, e: Int): Double + +/** + * Takes a String and an integer exponent. The String should hold a positive + * integer value (or zero). The exponent will be used to calculate the + * floating point number by taking the positive integer the String + * represents and multiplying by 10 raised to the power of the + * exponent. Returns the closest float value to the real number. + + * @param s + * * the String that will be parsed to a floating point + * * + * @param e + * * an int represent the 10 to part + * * + * @return the float closest to the real number + * * + * * + * @exception NumberFormatException + * * if the String doesn't represent a positive integer value + */ +@SymbolName("Konan_FloatingPointParser_parseFloatImpl") +private external fun parseFloatImpl(s: String, e: Int): Float + +/** + * Used to parse a string and return either a single or double precision + * floating point number. + */ +object FloatingPointParser { + /* + * All number with exponent larger than MAX_EXP can be treated as infinity. + * All number with exponent smaller than MIN_EXP can be treated as zero. + * Exponent is 10 based. + * Eg. double's min value is 5e-324, so double "1e-325" should be parsed as 0.0 + */ + private val FLOAT_MIN_EXP = -46 + private val FLOAT_MAX_EXP = 38 + private val DOUBLE_MIN_EXP = -324 + private val DOUBLE_MAX_EXP = 308 + + private class StringExponentPair(var s: String, var e: Int, var negative: Boolean) + + /** + * Takes a String and does some initial parsing. Should return a + * StringExponentPair containing a String with no leading or trailing white + * space and trailing zeroes eliminated. The exponent of the + * StringExponentPair will be used to calculate the floating point number by + * taking the positive integer the String represents and multiplying by 10 + * raised to the power of the exponent. + + * @param s + * * the String that will be parsed to a floating point + * * + * @param length + * * the length of s + * * + * @return a StringExponentPair with necessary values + * * + * * + * @exception NumberFormatException + * * if the String doesn't pass basic tests + */ + private fun initialParse(s: String, length: Int): StringExponentPair { + var s = s + var length = length + var negative = false + var c: Char + var start: Int + var end: Int + val decimal: Int + var shift: Int + var e = 0 + + start = 0 + if (length == 0) + throw NumberFormatException(s) + + c = s[length - 1] + if (c == 'D' || c == 'd' || c == 'F' || c == 'f') { + length-- + if (length == 0) + throw NumberFormatException(s) + } + + end = maxOf(s.indexOf('E'), s.indexOf('e')) + if (end > -1) { + if (end + 1 == length) + throw NumberFormatException(s) + + var exponent_offset = end + 1 + if (s[exponent_offset] == '+') { + if (s[exponent_offset + 1] == '-') { + throw NumberFormatException(s) + } + exponent_offset++ // skip the plus sign + if (exponent_offset == length) + throw NumberFormatException(s) + } + val strExp = s.substring(exponent_offset, length) + try { + e = strExp.toInt() + } catch (ex: NumberFormatException) { + // strExp is not empty, so there are 2 situations the exception be thrown + // if the string is invalid we should throw exception, if the actual number + // is out of the range of Integer, we can still parse the original number to + // double or float. + var ch: Char + for (i in 0..strExp.length - 1) { + ch = strExp[i] + if (ch < '0' || ch > '9') { + if (i == 0 && ch == '-') + continue + // ex contains the exponent substring only so throw + // a new exception with the correct string. + throw NumberFormatException(s) + } + } + e = if (strExp[0] == '-') Int.MIN_VALUE else Int.MAX_VALUE + } + + } else { + end = length + } + if (length == 0) + throw NumberFormatException(s) + + c = s[start] + if (c == '-') { + ++start + --length + negative = true + } else if (c == '+') { + ++start + --length + } + if (length == 0) + throw NumberFormatException(s) + + decimal = s.indexOf('.') + if (decimal > -1) { + shift = end - decimal - 1 + // Prevent e overflow, shift >= 0. + if (e >= 0 || e - Int.MIN_VALUE > shift) { + e -= shift + } + s = s.substring(start, decimal) + s.substring(decimal + 1, end) + } else { + s = s.substring(start, end) + } + + length = s.length + if (length == 0) + throw NumberFormatException() + + end = length + while (end > 1 && s[end - 1] == '0') + --end + + start = 0 + while (start < end - 1 && s[start] == '0') + start++ + + if (end != length || start != 0) { + shift = length - end + if (e <= 0 || Int.MAX_VALUE - e > shift) { + e += shift + } + s = s.substring(start, end) + } + + // Trim the length of very small numbers, natives can only handle down to E-309. + val APPROX_MIN_MAGNITUDE = -359 + val MAX_DIGITS = 52 + length = s.length + if (length > MAX_DIGITS && e < APPROX_MIN_MAGNITUDE) { + val d = minOf(APPROX_MIN_MAGNITUDE - e, length - 1) + s = s.substring(0, length - d) + e += d + } + + return StringExponentPair(s, e, negative) + } + + /* + * Assumes the string is trimmed. + */ + private fun parseDoubleName(namedDouble: String, length: Int): Double { + // Valid strings are only +Nan, NaN, -Nan, +Infinity, Infinity, -Infinity. + if (length != 3 && length != 4 && length != 8 && length != 9) { + throw NumberFormatException() + } + + var negative = false + var cmpstart = 0 + when (namedDouble[0]) { + '-' -> { + negative = true + cmpstart = 1 + } + '+' -> cmpstart = 1 + } + + if (namedDouble.regionMatches(cmpstart, "Infinity", 0, 8, ignoreCase = false)) { + return if (negative) + Double.NEGATIVE_INFINITY + else + Double.POSITIVE_INFINITY + } + + if (namedDouble.regionMatches(cmpstart, "NaN", 0, 3, ignoreCase = false)) { + return Double.NaN + } + + throw NumberFormatException() + } + + /* + * Assumes the string is trimmed. + */ + private fun parseFloatName(namedFloat: String, length: Int): Float { + // Valid strings are only +Nan, NaN, -Nan, +Infinity, Infinity, -Infinity. + if (length != 3 && length != 4 && length != 8 && length != 9) { + throw NumberFormatException() + } + + var negative = false + var cmpstart = 0 + when (namedFloat[0]) { + '-' -> { + negative = true + cmpstart = 1 + } + '+' -> cmpstart = 1 + } + + if (namedFloat.regionMatches(cmpstart, "Infinity", 0, 8, ignoreCase = false)) { + return if (negative) Float.NEGATIVE_INFINITY else Float.POSITIVE_INFINITY + } + + if (namedFloat.regionMatches(cmpstart, "NaN", 0, 3, ignoreCase = false)) { + return Float.NaN + } + + throw NumberFormatException() + } + + /* + * Answers true if the string should be parsed as a hex encoding. + * Assumes the string is trimmed. + */ + private fun parseAsHex(s: String): Boolean { + val length = s.length + if (length < 2) { + return false + } + var first = s[0] + var second = s[1] + if (first == '+' || first == '-') { + // Move along. + if (length < 3) { + return false + } + first = second + second = s[2] + } + return first == '0' && (second == 'x' || second == 'X') + } + + /** + * Returns the closest double value to the real number in the string. + + * @param s + * * the String that will be parsed to a floating point + * * + * @return the double closest to the real number + * * + * * + * @exception NumberFormatException + * * if the String doesn't represent a double + */ + fun parseDouble(s: String): Double { + var s = s + s = s.trim { it <= ' ' } + val length = s.length + + if (length == 0) { + throw NumberFormatException(s) + } + + // See if this could be a named double. + val last = s[length - 1] + if (last == 'y' || last == 'N') { + return parseDoubleName(s, length) + } + + // See if it could be a hexadecimal representation. + if (parseAsHex(s)) { + TODO("Hex format is not supported") + //return HexStringParser.parseDouble(s) + } + + val info = initialParse(s, length) + + // Two kinds of situation will directly return 0.0: + // 1. info.s is 0; + // 2. actual exponent is less than Double.MIN_EXPONENT. + if ("0" == info.s || info.e + info.s.length - 1 < DOUBLE_MIN_EXP) { + return if (info.negative) -0.0 else 0.0 + } + // If actual exponent is larger than Double.MAX_EXPONENT, return infinity. + // Prevent overflow, check twice. + if (info.e > DOUBLE_MAX_EXP || info.e + info.s.length - 1 > DOUBLE_MAX_EXP) { + return if (info.negative) Double.NEGATIVE_INFINITY else Double.POSITIVE_INFINITY + } + var result = parseDoubleImpl(info.s, info.e) + if (info.negative) + result = -result + + return result + } + + /** + * Returns the closest float value to the real number in the string. + + * @param s + * * the String that will be parsed to a floating point + * * + * @return the float closest to the real number + * * + * * + * @exception NumberFormatException + * * if the String doesn't represent a float + */ + fun parseFloat(s: String): Float { + var s = s + s = s.trim { it <= ' ' } + val length = s.length + + if (length == 0) { + throw NumberFormatException(s) + } + + // See if this could be a named float. + val last = s[length - 1] + if (last == 'y' || last == 'N') { + return parseFloatName(s, length) + } + + // See if it could be a hexadecimal representation. + if (parseAsHex(s)) { + TODO("Hex format is not supported") + //return HexStringParser.parseFloat(s) + } + + val info = initialParse(s, length) + + // Two kinds of situation will directly return 0.0f. + // 1. info.s is 0; + // 2. actual exponent is less than Float.MIN_EXPONENT. + if ("0" == info.s || info.e + info.s.length - 1 < FLOAT_MIN_EXP) { + return if (info.negative) -0.0f else 0.0f + } + // If actual exponent is larger than Float.MAX_EXPONENT, return infinity. + // Prevent overflow, check twice. + if (info.e > FLOAT_MAX_EXP || info.e + info.s.length - 1 > FLOAT_MAX_EXP) { + return if (info.negative) Float.NEGATIVE_INFINITY else Float.POSITIVE_INFINITY + } + var result = parseFloatImpl(info.s, info.e) + if (info.negative) + result = -result + + return result + } +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/konan/internal/HexStringParser.kt b/runtime/src/main/kotlin/konan/internal/HexStringParser.kt new file mode 100644 index 00000000000..3653151b038 --- /dev/null +++ b/runtime/src/main/kotlin/konan/internal/HexStringParser.kt @@ -0,0 +1,366 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package konan.internal + +// TODO: Enable as soon as regexes are supported. + +/* + * Parses hex string to a single or double precision floating point number. + */ +//internal class HexStringParser(private val EXPONENT_WIDTH: Int, private val MANTISSA_WIDTH: Int) { +// +// private val EXPONENT_BASE: Long +// +// private val MAX_EXPONENT: Long +// +// private val MIN_EXPONENT: Long +// +// private val MANTISSA_MASK: Long +// +// private var sign: Long = 0 +// +// private var exponent: Long = 0 +// +// private var mantissa: Long = 0 +// +// private var abandonedNumber = "" //$NON-NLS-1$ +// +// init { +// +// this.EXPONENT_BASE = (-1L shl EXPONENT_WIDTH - 1).inv() +// this.MAX_EXPONENT = (-1L shl EXPONENT_WIDTH).inv() +// this.MIN_EXPONENT = (-(MANTISSA_WIDTH + 1)).toLong() +// this.MANTISSA_MASK = (-1L shl MANTISSA_WIDTH).inv() +// } +// +// private fun parse(hexString: String): Long { +// val hexSegments = getSegmentsFromHexString(hexString) +// val signStr = hexSegments[0] +// val significantStr = hexSegments[1] +// val exponentStr = hexSegments[2] +// +// parseHexSign(signStr) +// parseExponent(exponentStr) +// parseMantissa(significantStr) +// +// sign = sign shl (MANTISSA_WIDTH + EXPONENT_WIDTH) +// exponent = exponent shl MANTISSA_WIDTH +// return sign or exponent or mantissa +// } +// +// /* +// * Parses the sign field. +// */ +// private fun parseHexSign(signStr: String) { +// this.sign = (if (signStr == "-") 1 else 0).toLong() //$NON-NLS-1$ +// } +// +// /* +// * Parses the exponent field. +// */ +// private fun parseExponent(exponentStr: String) { +// var exponentStr = exponentStr +// val leadingChar = exponentStr[0] +// val expSign = if (leadingChar == '-') -1 else 1 +// if (!Character.isDigit(leadingChar)) { +// exponentStr = exponentStr.substring(1) +// } +// +// try { +// exponent = expSign * exponentStr.toLong() +// checkedAddExponent(EXPONENT_BASE) +// } catch (e: NumberFormatException) { +// exponent = expSign * Long.MAX_VALUE +// } +// +// } +// +// /* +// * Parses the mantissa field. +// */ +// private fun parseMantissa(significantStr: String) { +// val strings = significantStr.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() //$NON-NLS-1$ +// val strIntegerPart = strings[0] +// val strDecimalPart = if (strings.size > 1) strings[1] else "" //$NON-NLS-1$ +// +// var significand = getNormalizedSignificand(strIntegerPart, strDecimalPart) +// if (significand == "0") { //$NON-NLS-1$ +// setZero() +// return +// } +// +// val offset = getOffset(strIntegerPart, strDecimalPart) +// checkedAddExponent(offset.toLong()) +// +// if (exponent >= MAX_EXPONENT) { +// setInfinite() +// return +// } +// +// if (exponent <= MIN_EXPONENT) { +// setZero() +// return +// } +// +// if (significand.length > MAX_SIGNIFICANT_LENGTH) { +// abandonedNumber = significand.substring(MAX_SIGNIFICANT_LENGTH) +// significand = significand.substring(0, MAX_SIGNIFICANT_LENGTH) +// } +// +// mantissa = significand.toLong(HEX_RADIX) +// +// if (exponent >= 1) { +// processNormalNumber() +// } else { +// processSubNormalNumber() +// } +// +// } +// +// private fun setInfinite() { +// exponent = MAX_EXPONENT +// mantissa = 0 +// } +// +// private fun setZero() { +// exponent = 0 +// mantissa = 0 +// } +// +// private fun signum(x: Long) = when { +// x == 0L -> 0 +// x > 0L -> 1 +// else -> -1 +// } +// +// /* +// * Sets the exponent variable to Long.MAX_VALUE or -Long.MAX_VALUE if +// * overflow or underflow happens. +// */ +// private fun checkedAddExponent(offset: Long) { +// val result = exponent + offset +// val expSign = signum(exponent) +// if (expSign * signum(offset) > 0 && expSign * signum(result) < 0) { +// exponent = expSign * Long.MAX_VALUE +// } else { +// exponent = result +// } +// } +// +// private fun processNormalNumber() { +// val desiredWidth = MANTISSA_WIDTH + 2 +// fitMantissaInDesiredWidth(desiredWidth) +// round() +// mantissa = mantissa and MANTISSA_MASK +// } +// +// private fun processSubNormalNumber() { +// var desiredWidth = MANTISSA_WIDTH + 1 +// desiredWidth += exponent.toInt()//lends bit from mantissa to exponent +// exponent = 0 +// fitMantissaInDesiredWidth(desiredWidth) +// round() +// mantissa = mantissa and MANTISSA_MASK +// } +// +// /* +// * Adjusts the mantissa to desired width for further analysis. +// */ +// private fun fitMantissaInDesiredWidth(desiredWidth: Int) { +// val bitLength = countBitsLength(mantissa) +// if (bitLength > desiredWidth) { +// discardTrailingBits((bitLength - desiredWidth).toLong()) +// } else { +// mantissa = mantissa shl (desiredWidth - bitLength) +// } +// } +// +// /* +// * Stores the discarded bits to abandonedNumber. +// */ +// private fun discardTrailingBits(num: Long) { +// val mask = (-1L shl num.toInt()).inv() +// abandonedNumber += mantissa and mask +// mantissa = mantissa shr num.toInt() +// } +// +// /* +// * The value is rounded up or down to the nearest infinitely precise result. +// * If the value is exactly halfway between two infinitely precise results, +// * then it should be rounded up to the nearest infinitely precise even. +// */ +// private fun round() { +// val result = abandonedNumber.replace("0+".toRegex(), "") //$NON-NLS-1$ //$NON-NLS-2$ +// val moreThanZero = result.length > 0 +// +// val lastDiscardedBit = (mantissa and 1L).toInt() +// mantissa = mantissa shr 1 +// val tailBitInMantissa = (mantissa and 1L).toInt() +// +// if (lastDiscardedBit == 1 && (moreThanZero || tailBitInMantissa == 1)) { +// val oldLength = countBitsLength(mantissa) +// mantissa += 1L +// val newLength = countBitsLength(mantissa) +// +// //Rounds up to exponent when whole bits of mantissa are one-bits. +// if (oldLength >= MANTISSA_WIDTH && newLength > oldLength) { +// checkedAddExponent(1) +// } +// } +// } +// +// /* +// * Returns the normalized significand after removing the leading zeros. +// */ +// private fun getNormalizedSignificand(strIntegerPart: String, strDecimalPart: String): String { +// var significand = strIntegerPart + strDecimalPart +// significand = significand.replaceFirst("^0+".toRegex(), "") //$NON-NLS-1$//$NON-NLS-2$ +// if (significand.length == 0) { +// significand = "0" //$NON-NLS-1$ +// } +// return significand +// } +// +// /* +// * Calculates the offset between the normalized number and unnormalized +// * number. In a normalized representation, significand is represented by the +// * characters "0x1." followed by a lowercase hexadecimal representation of +// * the rest of the significand as a fraction. +// */ +// private fun getOffset(strIntegerPart: String, strDecimalPart: String): Int { +// var strIntegerPart = strIntegerPart +// strIntegerPart = strIntegerPart.replaceFirst("^0+".toRegex(), "") //$NON-NLS-1$ //$NON-NLS-2$ +// +// // If the Integer part is a nonzero number. +// if (strIntegerPart.length != 0) { +// val leadingNumber = strIntegerPart.substring(0, 1) +// return (strIntegerPart.length - 1) * 4 + countBitsLength(leadingNumber.toLong(HEX_RADIX)) - 1 +// } +// +// // If the Integer part is a zero number. +// var i = 0 +// while (i < strDecimalPart.length && strDecimalPart[i] == '0') { +// i++ +// } +// if (i == strDecimalPart.length) { +// return 0 +// } +// val leadingNumber = strDecimalPart.substring(i, i + 1) +// return (-i - 1) * 4 + countBitsLength(leadingNumber.toLong(HEX_RADIX)) - 1 +// } +// +// fun numberOfLeadingZeros(i: Long): Int { +// // HD, Figure 5-6 +// if (i == 0L) +// return 64 +// var n = 1 +// var x = (i ushr 32).toInt() +// if (x == 0) { +// n += 32 +// x = i.toInt() +// } +// if (x ushr 16 == 0) { +// n += 16 +// x = x shl 16 +// } +// if (x ushr 24 == 0) { +// n += 8 +// x = x shl 8 +// } +// if (x ushr 28 == 0) { +// n += 4 +// x = x shl 4 +// } +// if (x ushr 30 == 0) { +// n += 2 +// x = x shl 2 +// } +// n -= x ushr 31 +// return n +// } +// +// private fun countBitsLength(value: Long): Int { +// val leadingZeros = numberOfLeadingZeros(value) +// return java.lang.Long.SIZE - leadingZeros +// } +// +// companion object { +// +// private val DOUBLE_EXPONENT_WIDTH = 11 +// +// private val DOUBLE_MANTISSA_WIDTH = 52 +// +// private val FLOAT_EXPONENT_WIDTH = 8 +// +// private val FLOAT_MANTISSA_WIDTH = 23 +// +// private val HEX_RADIX = 16 +// +// private val MAX_SIGNIFICANT_LENGTH = 15 +// +// private val HEX_SIGNIFICANT = "0[xX](\\p{XDigit}+\\.?|\\p{XDigit}*\\.\\p{XDigit}+)" //$NON-NLS-1$ +// +// private val BINARY_EXPONENT = "[pP]([+-]?\\d+)" //$NON-NLS-1$ +// +// private val FLOAT_TYPE_SUFFIX = "[fFdD]?" //$NON-NLS-1$ +// +// private val HEX_PATTERN = "[\\x00-\\x20]*([+-]?)$HEX_SIGNIFICANT" + //$NON-NLS-1$ +// +// BINARY_EXPONENT + FLOAT_TYPE_SUFFIX + "[\\x00-\\x20]*" //$NON-NLS-1$ +// +// private val PATTERN = Pattern.compile(HEX_PATTERN) +// +// /* +// * Parses the hex string to a double number. +// */ +// fun parseDouble(hexString: String): Double { +// val parser = HexStringParser(DOUBLE_EXPONENT_WIDTH, +// DOUBLE_MANTISSA_WIDTH) +// val result = parser.parse(hexString) +// return java.lang.Double.longBitsToDouble(result) +// } +// +// /* +// * Parses the hex string to a float number. +// */ +// fun parseFloat(hexString: String): Float { +// val parser = HexStringParser(FLOAT_EXPONENT_WIDTH, +// FLOAT_MANTISSA_WIDTH) +// val result = parser.parse(hexString).toInt() +// return java.lang.Float.intBitsToFloat(result) +// } +// +// /* +// * Analyzes the hex string and extracts the sign and digit segments. +// */ +// private fun getSegmentsFromHexString(hexString: String): Array { +// val matcher = PATTERN.matcher(hexString) +// if (!matcher.matches()) { +// throw NumberFormatException() +// } +// +// val hexSegments = arrayOf( +// matcher.group(1), +// matcher.group(2), +// matcher.group(3) +// ) +// +// return hexSegments +// } +// } +//} \ No newline at end of file diff --git a/runtime/src/main/kotlin/konan/internal/NumberConverter.kt b/runtime/src/main/kotlin/konan/internal/NumberConverter.kt new file mode 100644 index 00000000000..067e5e38d26 --- /dev/null +++ b/runtime/src/main/kotlin/konan/internal/NumberConverter.kt @@ -0,0 +1,310 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package konan.internal + +@SymbolName("Konan_NumberConverter_bigIntDigitGeneratorInstImpl") +private external fun bigIntDigitGeneratorInstImpl(results: IntArray, uArray: IntArray, f: Long, e: Int, + isDenormalized: Boolean, mantissaIsZero: Boolean, p: Int) + +@SymbolName("Konan_NumberConverter_ceil") +private external fun ceil(x: Double): Double + +class NumberConverter { + + private var setCount: Int = 0 // Number of times u and k have been gotten. + + private var getCount: Int = 0 // Number of times u and k have been set. + + private val uArray = IntArray(64) + + private var firstK: Int = 0 + + private fun convertDouble(inputNumber: Double): String { + val p = 1023 + 52 // The power offset (precision). + val signMask = 0x7FFFFFFFFFFFFFFFL + 1 // The mask to get the sign of. + // The number. + val eMask = 0x7FF0000000000000L // The mask to get the power bits. + val fMask = 0x000FFFFFFFFFFFFFL // The mask to get the significand. + + // Bits. + val inputNumberBits = inputNumber.bits() + // The value of the sign... 0 is positive, ~0 is negative. + val signString = if (inputNumberBits and signMask == 0L) "" else "-" + // The value of the 'power bits' of the inputNumber. + val e = (inputNumberBits and eMask shr 52).toInt() + // The value of the 'significand bits' of the inputNumber. + var f = inputNumberBits and fMask + val mantissaIsZero = f == 0L + var pow = 0 + var numBits = 52 + + if (e == 2047) + return if (mantissaIsZero) signString + "Infinity" else "NaN" + if (e == 0) { + if (mantissaIsZero) + return signString + "0.0" + if (f == 1L) + // Special case to increase precision even though 2 * Double.MIN_VALUE is 1.0e-323. + return signString + "4.9E-324" + pow = 1 - p // A denormalized number. + var ff = f + while (ff and 0x0010000000000000L == 0L) { + ff = ff shl 1 + numBits-- + } + } else { + // 0 < e < 2047. + // A "normalized" number. + f = f or 0x0010000000000000L + pow = e - p + } + + if (-59 < pow && pow < 6 || pow == -59 && !mantissaIsZero) + longDigitGenerator(f, pow, e == 0, mantissaIsZero, numBits) + else + bigIntDigitGeneratorInstImpl(f, pow, e == 0, mantissaIsZero, numBits) + + if (inputNumber >= 1e7 || inputNumber <= -1e7 + || inputNumber > -1e-3 && inputNumber < 1e-3) + return signString + freeFormatExponential() + + return signString + freeFormat() + } + + private fun convertFloat(inputNumber: Float): String { + val p = 127 + 23 // The power offset (precision). + val signMask = 0x7FFFFFFF + 1 // The mask to get the sign of the number. + val eMask = 0x7F800000 // The mask to get the power bits. + val fMask = 0x007FFFFF // The mask to get the significand bits. + + val inputNumberBits = inputNumber.bits() + // The value of the sign... 0 is positive, ~0 is negative. + val signString = if (inputNumberBits and signMask == 0) "" else "-" + // The value of the 'power bits' of the inputNumber. + val e = inputNumberBits and eMask shr 23 + // The value of the 'significand bits' of the inputNumber. + var f = inputNumberBits and fMask + val mantissaIsZero = f == 0 + var pow = 0 + var numBits = 23 + + if (e == 255) + return if (mantissaIsZero) signString + "Infinity" else "NaN" + if (e == 0) { + if (mantissaIsZero) + return signString + "0.0" + pow = 1 - p // A denormalized number. + if (f < 8) { // Want more precision with smallest values. + f = f shl 2 + pow -= 2 + } + var ff = f + while (ff and 0x00800000 == 0) { + ff = ff shl 1 + numBits-- + } + } else { + // 0 < e < 255. + // A "normalized" number. + f = f or 0x00800000 + pow = e - p + } + + if (-59 < pow && pow < 35 || pow == -59 && !mantissaIsZero) + longDigitGenerator(f.toLong(), pow, e == 0, mantissaIsZero, numBits) + else + bigIntDigitGeneratorInstImpl(f.toLong(), pow, e == 0, mantissaIsZero, numBits) + if (inputNumber >= 1e7f || inputNumber <= -1e7f + || inputNumber > -1e-3f && inputNumber < 1e-3f) + return signString + freeFormatExponential() + + return signString + freeFormat() + } + + private fun freeFormatExponential(): String { + // Corresponds to process "Free-Format Exponential". + val formattedDecimal = CharArray(25) + formattedDecimal[0] = ('0' + uArray[getCount++]) + formattedDecimal[1] = '.' + // The position the next character is to be inserted into formattedDecimal. + var charPos = 2 + + var k = firstK + val expt = k + while (true) { + k-- + if (getCount >= setCount) + break + + formattedDecimal[charPos++] = ('0' + uArray[getCount++]) + } + + if (k == expt - 1) + formattedDecimal[charPos++] = '0' + formattedDecimal[charPos++] = 'E' + return fromCharArray(formattedDecimal, 0, charPos) + expt.toString() + } + + private fun freeFormat(): String { + // Corresponds to process "Free-Format". + val formattedDecimal = CharArray(25) + // The position the next character is to be inserted into formattedDecimal. + var charPos = 0 + var k = firstK + if (k < 0) { + formattedDecimal[0] = '0' + formattedDecimal[1] = '.' + charPos += 2 + for (i in k + 1 .. -1) + formattedDecimal[charPos++] = '0' + } + + var u = uArray[getCount++] + do { + if (u != -1) + formattedDecimal[charPos++] = ('0' + u) + else if (k >= -1) + formattedDecimal[charPos++] = '0' + + if (k == 0) + formattedDecimal[charPos++] = '.' + + k-- + u = if (getCount < setCount) uArray[getCount++] else -1 + } while (u != -1 || k >= -1) + return fromCharArray(formattedDecimal, 0, charPos) + } + + private fun bigIntDigitGeneratorInstImpl(f: Long, e: Int, + isDenormalized: Boolean, mantissaIsZero: Boolean, p: Int) { + val results = IntArray(3) + bigIntDigitGeneratorInstImpl(results, uArray, f, e, isDenormalized, mantissaIsZero, p) + setCount = results[0] + getCount = results[1] + firstK = results[2] + } + + private fun longDigitGenerator(f: Long, e: Int, isDenormalized: Boolean, + mantissaIsZero: Boolean, p: Int) { + var r: Long + var s: Long + var m: Long + if (e >= 0) { + m = 1L shl e + if (!mantissaIsZero) { + r = f shl e + 1 + s = 2 + } else { + r = f shl e + 2 + s = 4 + } + } else { + m = 1 + if (isDenormalized || !mantissaIsZero) { + r = f shl 1 + s = 1L shl 1 - e + } else { + r = f shl 2 + s = 1L shl 2 - e + } + } + + val k = ceil((e + p - 1) * invLogOfTenBaseTwo - 1e-10).toInt() + + if (k > 0) { + s *= TEN_TO_THE[k] + } else if (k < 0) { + val scale = TEN_TO_THE[-k] + r *= scale + m = if (m == 1L) scale else m * scale + } + + if (r + m > s) { // Was M_plus. + firstK = k + } else { + firstK = k - 1 + r *= 10 + m *= 10 + } + + setCount = 0 + getCount = setCount // Reset indices. + var low: Boolean + var high: Boolean + var u: Int + val si = longArrayOf(s, s shl 1, s shl 2, s shl 3) + while (true) { + // Set U to be floor (r / s) and r to be the remainder + // using a kind of "binary search" to find the answer. + // It's a lot quicker than actually dividing since we know + // the answer will be between 0 and 10. + u = 0 + var remainder: Long + for (i in 3 downTo 0) { + remainder = r - si[i] + if (remainder >= 0) { + r = remainder + u += 1 shl i + } + } + + low = r < m // Was M_minus. + high = r + m > s // Was M_plus. + + if (low || high) + break + + r *= 10 + m *= 10 + uArray[setCount++] = u + } + if (low && !high) + uArray[setCount++] = u + else if (high && !low) + uArray[setCount++] = u + 1 + else if (r shl 1 < s) + uArray[setCount++] = u + else + uArray[setCount++] = u + 1 + } + + companion object { + + private val invLogOfTenBaseTwo = 0.30102999566398114251 + + private val TEN_TO_THE = LongArray(20) + + init { + TEN_TO_THE[0] = 1L + for (i in 1 until TEN_TO_THE.size) { + TEN_TO_THE[i] = TEN_TO_THE[i - 1] * 10 + } + } + + private val converter: NumberConverter + get() = NumberConverter() + + fun convert(input: Double): String { + return converter.convertDouble(input) + } + + fun convert(input: Float): String { + return converter.convertFloat(input) + } + } +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt index b9cac57d59d..dd72ccf93c1 100644 --- a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt +++ b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt @@ -45,6 +45,11 @@ internal fun ThrowNumberFormatException() : Nothing { throw NumberFormatException() } +@ExportForCppRuntime +internal fun ThrowOutOfMemoryError() : Nothing { + throw OutOfMemoryError() +} + fun ThrowNoWhenBranchMatchedException(): Nothing { throw NoWhenBranchMatchedException() } diff --git a/runtime/src/main/kotlin/kotlin/Primitives.kt b/runtime/src/main/kotlin/kotlin/Primitives.kt index b77eeba89de..01dbca7991b 100644 --- a/runtime/src/main/kotlin/kotlin/Primitives.kt +++ b/runtime/src/main/kotlin/kotlin/Primitives.kt @@ -16,6 +16,8 @@ package kotlin +import konan.internal.NumberConverter + /** * Represents a 8-bit signed integer. * On the JVM, non-nullable values of this type are represented as values of the primitive type `byte`. @@ -1151,8 +1153,7 @@ public final class Float : Number(), Comparable { public override fun equals(other: Any?): Boolean = other is Float && konan.internal.areEqualByValue(this, other) - @SymbolName("Kotlin_Float_toString") - external public override fun toString(): String + public override fun toString() = NumberConverter.convert(this) public override fun hashCode(): Int { return bits() @@ -1371,8 +1372,7 @@ public final class Double : Number(), Comparable { public override fun equals(other: Any?): Boolean = other is Double && konan.internal.areEqualByValue(this, other) - @SymbolName("Kotlin_Double_toString") - external public override fun toString(): String + public override fun toString() = NumberConverter.convert(this) public override fun hashCode(): Int { return bits().hashCode() diff --git a/runtime/src/main/kotlin/kotlin/text/StringNumberConversions.kt b/runtime/src/main/kotlin/kotlin/text/StringNumberConversions.kt index 43e821ab430..551a6c58174 100644 --- a/runtime/src/main/kotlin/kotlin/text/StringNumberConversions.kt +++ b/runtime/src/main/kotlin/kotlin/text/StringNumberConversions.kt @@ -16,6 +16,8 @@ package kotlin.text +import konan.internal.FloatingPointParser + /** * Returns a string representation of this [Byte] value in the specified [radix]. */ @@ -118,28 +120,22 @@ public inline fun String.toLong(): Long = toLongOrNull() ?: throw NumberFormatEx @kotlin.internal.InlineOnly public inline fun String.toLong(radix: Int): Long = toLongOrNull(radix) ?: throw NumberFormatException() -@SymbolName("Kotlin_String_parseFloat") -external private fun parseFloat(value: String): Float - /** * Parses the string as a [Float] number and returns the result. * @throws NumberFormatException if the string is not a valid representation of a number. */ @kotlin.internal.InlineOnly @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") -public inline fun String.toFloat(): Float = parseFloat(this) +public inline fun String.toFloat(): Float = FloatingPointParser.parseFloat(this) -@SymbolName("Kotlin_String_parseDouble") -external private fun parseDouble(value: String): Double - /** * Parses the string as a [Double] number and returns the result. * @throws NumberFormatException if the string is not a valid representation of a number. */ @kotlin.internal.InlineOnly @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") -public inline fun String.toDouble(): Double = parseDouble(this) +public inline fun String.toDouble(): Double = FloatingPointParser.parseDouble(this) /**