diff --git a/docs/manual/functions.qmd b/docs/manual/functions.qmd index 6d3f560..5d94c3b 100644 --- a/docs/manual/functions.qmd +++ b/docs/manual/functions.qmd @@ -10,19 +10,6 @@ The following built-in functions are available: | ASIN(Number) | Returns the arcsine, or inverse sine function, of *Number*, where -1 <= *Number* <= 1. The arcsine is the angle whose sine is *Number*. The returned angle is given in radians where -pi/2 <= angle <= pi/2. | | ATAN(x) | Returns the principal value of the arc tangent of *x*, expressed in radians.. | | ATAN2(y, x) | Returns the principal value of the arc tangent of *y*,*x*, expressed in radians. | -| BITAND(Number1, Number2) | Returns a bitwise 'AND' of two (integral) numbers. (Both numbers must be positive.) | -| BITLROTATE8(Number, RotateAmount) | Returns *Number* left rotated left to the most significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 8-bit integer.
\linebreak (Only available if compiled as C++20.) | -| BITLROTATE16(Number, RotateAmount) | Returns *Number* left rotated left to the most significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 16-bit integer.
\linebreak (Only available if compiled as C++20.) | -| BITLROTATE32(Number, RotateAmount) | Returns *Number* left rotated left to the most significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 32-bit integer.
\linebreak (Only available if compiled as C++20.) | -| BITLROTATE64(Number, RotateAmount) | Returns *Number* left rotated left to the most significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 64-bit integer.
\linebreak Note, however, that values beyond the range of `double` should not be used as they will wrap around.
\linebreak (Only available if compiled as C++20.) | -| BITLSHIFT(Number, ShiftAmount) | Returns *Number* left shifted by the specified number (*ShiftAmount*) of bits. | -| BITOR(Number1, Number2) | Returns a bitwise 'OR' of two (integral) numbers. (Both numbers must be positive.) | -| BITRROTATE8(Number, RotateAmount) | Returns *Number* right rotated right to the least significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 8-bit integer.
\linebreak (Only available if compiled as C++20.) | -| BITRROTATE16(Number, RotateAmount) | Returns *Number* right rotated right to the least significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 16-bit integer.
\linebreak (Only available if compiled as C++20.) | -| BITRROTATE32(Number, RotateAmount) | Returns *Number* right rotated right to the least significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 32-bit integer.
\linebreak (Only available if compiled as C++20.) | -| BITRROTATE64(Number, RotateAmount) | Returns *Number* right rotated right to the least significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 64-bit integer.
\linebreak Note, however, that values beyond the range of `double` should not be used as they will wrap around.
\linebreak (Only available if compiled as C++20.) | -| BITRSHIFT(Number, ShiftAmount) | Returns *Number* right shifted by the specified number (*ShiftAmount*) of bits. | -| BITXOR(Number1, Number2) | Returns a bitwise 'XOR' of two (integral) numbers. (Both numbers must be positive.) | | CEIL(Number) | Smallest integer not less than *Number*.
\linebreak `CEIL(-3.2)` = -3
\linebreak `CEIL(3.2)` = 4 | | CLAMP(Number, Start, End) | Constrains *Number* within the range of *Start* and *End*. | | COMBIN(Number, NumberChosen) | Returns the number of combinations for a given number (*NumberChosen*) of items from *Number* of items. Note that for combinations, order of items is not important. | @@ -51,8 +38,6 @@ The following built-in functions are available: | SIN(Number) | Sine of the angle *Number* in radians. | | SINH(Number) | Hyperbolic sine of *Number*. | | SQRT(Number) | Square root of *Number*. | -| SUPPORTS32BIT() | Returns true if 32-bit integers are supported. This will affect the supported range of values for bitwise operations. | -| SUPPORTS64BIT() | Returns true if 64-bit integers are supported. This will affect the supported range of values for bitwise operations. | | TAN(Number) | Tangent of *Number*. | | TGAMMA(Number) | Returns the gamma function of *Number*. | | TRUNC(Number) | Discards the fractional part of *Number*.
\linebreak `TRUNC(-3.2)` = -3
\linebreak `TRUNC(3.2)` = 3 | @@ -60,6 +45,28 @@ The following built-in functions are available: Table: Math Functions\index{functions!math} ::: +::: {.minipage data-latex="{\textwidth}"} +| Function | Description | +| :-- | :-- | +| BITAND(Number1, Number2) | Returns a bitwise 'AND' of two (integral) numbers. (Both numbers must be positive and cannot exceed `(2^48)-1`.) | +| BITLROTATE8(Number, RotateAmount) | Returns *Number* left rotated left to the most significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 8-bit integer.
\linebreak (Only available if compiled as C++20.) | +| BITLROTATE16(Number, RotateAmount) | Returns *Number* left rotated left to the most significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 16-bit integer.
\linebreak (Only available if compiled as C++20.) | +| BITLROTATE32(Number, RotateAmount) | Returns *Number* left rotated left to the most significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 32-bit integer.
\linebreak (Only available if compiled as C++20.) | +| BITLROTATE(Number, RotateAmount) | Returns *Number* left rotated left to the most significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 53-bit (or 64-bit) integer.
\linebreak Note, however, that values beyond the range of `double` should not be used as they will wrap around.
\linebreak (Only available if compiled as C++20.) | +| BITLSHIFT(Number, ShiftAmount) | Returns *Number* left shifted by the specified number (*ShiftAmount*) of bits.
\linebreak *Number* cannot exceed `(2^48)-1` and *ShiftAmount* cannot exceed `53` (or `64`, if supported). | +| BITOR(Number1, Number2) | Returns a bitwise 'OR' of two (integral) numbers. (Both numbers must be positive and cannot exceed `(2^48)-1`.) | +| BITRROTATE8(Number, RotateAmount) | Returns *Number* right rotated right to the least significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 8-bit integer.
\linebreak (Only available if compiled as C++20.) | +| BITRROTATE16(Number, RotateAmount) | Returns *Number* right rotated right to the least significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 16-bit integer.
\linebreak (Only available if compiled as C++20.) | +| BITRROTATE32(Number, RotateAmount) | Returns *Number* right rotated right to the least significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 32-bit integer.
\linebreak (Only available if compiled as C++20.) | +| BITRROTATE(Number, RotateAmount) | Returns *Number* right rotated right to the least significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 53-bit (or 64-bit) integer.
\linebreak Note, however, that values beyond the range of `double` should not be used as they will wrap around.
\linebreak (Only available if compiled as C++20.) | +| BITRSHIFT(Number, ShiftAmount) | Returns *Number* right shifted by the specified number (*ShiftAmount*) of bits.
\linebreak *Number* cannot exceed `(2^48)-1` and *ShiftAmount* cannot exceed `53` (or `64`, if supported). | +| BITXOR(Number1, Number2) | Returns a bitwise 'XOR' of two (integral) numbers. (Both numbers must be positive and cannot exceed `(2^48)-1`.) | +| SUPPORTS32BIT() | Returns true if 32-bit integers are supported. This will affect the supported range of values for bitwise operations. | +| SUPPORTS64BIT() | Returns true if 64-bit integers are supported. This will affect the supported range of values for bitwise operations. | + +Table: Bitwise Functions\index{functions!bitwise} +::: + ::: {.notesection data-latex=""} Defining `TE_FLOAT` will disable all bitwise functions and operators. ::: diff --git a/tests/tetests.cpp b/tests/tetests.cpp index d7931a1..0576695 100644 --- a/tests/tetests.cpp +++ b/tests/tetests.cpp @@ -2179,6 +2179,9 @@ TEST_CASE("Bitwise operators", "[bitwise]") CHECK(tep.evaluate("8000 | 4294967295") == (8000 | 4294967295)); #endif + CHECK(tep.evaluate("=BITOR((2^48)-1, 1)") == 281474976710655); + CHECK(tep.evaluate("=BITOR((2^48)-1, (2^48)-1)") == 281474976710655); + CHECK(std::isnan(tep.evaluate("=BITOR((2^48)-1, (2^48))"))); CHECK(tep.evaluate("BITOR(23, 10)") == 31); CHECK(tep.evaluate("BITOR(23, 0)") == (23 | 0)); CHECK(tep.evaluate("BITOR(0, 10)") == (0 | 10)); @@ -2210,6 +2213,10 @@ TEST_CASE("Bitwise operators", "[bitwise]") CHECK(tep.evaluate("8000 ^ 4294967295") == (8000 ^ 4294967295)); #endif + CHECK(tep.evaluate("=BITXOR((2^48)-1, 1)") == 281474976710654); + CHECK(tep.evaluate("=BITXOR((2^48)-1, (2^48)-1)") == 0); + CHECK(tep.evaluate("=BITXOR((2^48)-1, 1587)") == 281474976709068); + CHECK(std::isnan(tep.evaluate("=BITXOR((2^48)-1, (2^48))"))); CHECK(tep.evaluate("BITXOR(5,3)") == 6); CHECK(tep.evaluate("BITXOR(5,9)") == 12); CHECK(tep.evaluate("BITXOR(23, 0)") == (23 ^ 0)); @@ -2233,6 +2240,10 @@ TEST_CASE("Bitwise operators", "[bitwise]") CHECK(tep.evaluate("23 & 0") == (23 & 0)); CHECK(tep.evaluate("0 & 10") == (0 & 10)); #endif + CHECK(tep.evaluate("=BITAND((2^48)-1, 1)") == 1); + CHECK(tep.evaluate("=BITAND((2^48)-1, (2^48)-1)") == 281474976710655); + CHECK(tep.evaluate("=BITAND((2^48)-1, 1587)") == 1587); + CHECK(std::isnan(tep.evaluate("=BITAND((2^48)-1, (2^48))"))); CHECK(tep.evaluate("BITAND(1, 5)") == 1); CHECK(tep.evaluate("BITAND(13, 25)") == 9); CHECK(tep.evaluate("BITAND(23, 0)") == (23 & 0)); @@ -2262,7 +2273,7 @@ TEST_CASE("Rotate operators", "[rotate]") CHECK(tep.evaluate("BITLROTATE8(255, 0)") == std::rotl(i, 0)); CHECK(tep.evaluate("BITLROTATE8(255, 1)") == std::rotl(i, 1)); CHECK(tep.evaluate("BITLROTATE8(255, 4)") == std::rotl(i, 4)); - CHECK(tep.evaluate("BITLROTATE8(255, 9)") == std::rotl(i, 9)); + CHECK(std::isnan(tep.evaluate("BITLROTATE8(255, 9)"))); CHECK(tep.evaluate("BITLROTATE8(255, -1)") == std::rotl(i, -1)); } SECTION("BITLROTATE16") @@ -2285,7 +2296,7 @@ TEST_CASE("Rotate operators", "[rotate]") CHECK(tep.evaluate("BITLROTATE32(4294967295, 9)") == std::rotl(i, 9)); CHECK(tep.evaluate("BITLROTATE32(4294967295, -1)") == std::rotl(i, -1)); } - SECTION("BITLROTATE64") + SECTION("BITLROTATE") { // malformed CHECK(std::isnan(tep.evaluate("5 <"))); @@ -2300,12 +2311,12 @@ TEST_CASE("Rotate operators", "[rotate]") CHECK(tep.evaluate("4294967295 <<< 9") == std::rotl(i, 9)); CHECK(tep.evaluate("4294967295 <<< -1") == std::rotl(i, -1)); - CHECK(tep.evaluate("BITLROTATE64(0, 0)") == std::rotl((uint64_t)0, 0)); - CHECK(tep.evaluate("BITLROTATE64(4294967295, 0)") == std::rotl(i, 0)); - CHECK(tep.evaluate("BITLROTATE64(4294967295, 1)") == std::rotl(i, 1)); - CHECK(tep.evaluate("BITLROTATE64(4294967295, 4)") == std::rotl(i, 4)); - CHECK(tep.evaluate("BITLROTATE64(4294967295, 9)") == std::rotl(i, 9)); - CHECK(tep.evaluate("BITLROTATE64(4294967295, -1)") == std::rotl(i, -1)); + CHECK(tep.evaluate("BITLROTATE(0, 0)") == std::rotl((uint64_t)0, 0)); + CHECK(tep.evaluate("BITLROTATE(4294967295, 0)") == std::rotl(i, 0)); + CHECK(tep.evaluate("BITLROTATE(4294967295, 1)") == std::rotl(i, 1)); + CHECK(tep.evaluate("BITLROTATE(4294967295, 4)") == std::rotl(i, 4)); + CHECK(tep.evaluate("BITLROTATE(4294967295, 9)") == std::rotl(i, 9)); + CHECK(tep.evaluate("BITLROTATE(4294967295, -1)") == std::rotl(i, -1)); } SECTION("BITRROTATE8") @@ -2315,7 +2326,7 @@ TEST_CASE("Rotate operators", "[rotate]") CHECK(tep.evaluate("BITRROTATE8(255, 0)") == std::rotr(i, 0)); CHECK(tep.evaluate("BITRROTATE8(255, 1)") == std::rotr(i, 1)); CHECK(tep.evaluate("BITRROTATE8(255, 4)") == std::rotr(i, 4)); - CHECK(tep.evaluate("BITRROTATE8(255, 9)") == std::rotr(i, 9)); + CHECK(std::isnan(tep.evaluate("BITRROTATE8(255, 9)"))); CHECK(tep.evaluate("BITRROTATE8(255, -1)") == std::rotr(i, -1)); } SECTION("BITRROTATE16") @@ -2338,7 +2349,7 @@ TEST_CASE("Rotate operators", "[rotate]") CHECK(tep.evaluate("BITRROTATE32(4294967295, 9)") == std::rotr(i, 9)); CHECK(tep.evaluate("BITRROTATE32(4294967295, -1)") == std::rotr(i, -1)); } - SECTION("BITRROTATE64") + SECTION("BITRROTATE") { // malformed CHECK(std::isnan(tep.evaluate("5 >"))); @@ -2355,12 +2366,12 @@ TEST_CASE("Rotate operators", "[rotate]") CHECK(tep.evaluate("4294967295 >>> 9") == std::rotr(i, 9)); CHECK(tep.evaluate("4294967295 >>> -1") == std::rotr(i, -1)); - CHECK(tep.evaluate("BITRROTATE64(0, 0)") == std::rotr((uint64_t)0, 0)); - CHECK(tep.evaluate("BITRROTATE64(4294967295, 0)") == std::rotr(i, 0)); - CHECK(tep.evaluate("BITRROTATE64(4294967295, 1)") == std::rotr(i, 1)); - CHECK(tep.evaluate("BITRROTATE64(4294967295, 4)") == std::rotr(i, 4)); - CHECK(tep.evaluate("BITRROTATE64(4294967295, 9)") == std::rotr(i, 9)); - CHECK(tep.evaluate("BITRROTATE64(4294967295, -1)") == std::rotr(i, -1)); + CHECK(tep.evaluate("BITRROTATE(0, 0)") == std::rotr((uint64_t)0, 0)); + CHECK(tep.evaluate("BITRROTATE(4294967295, 0)") == std::rotr(i, 0)); + CHECK(tep.evaluate("BITRROTATE(4294967295, 1)") == std::rotr(i, 1)); + CHECK(tep.evaluate("BITRROTATE(4294967295, 4)") == std::rotr(i, 4)); + CHECK(tep.evaluate("BITRROTATE(4294967295, 9)") == std::rotr(i, 9)); + CHECK(tep.evaluate("BITRROTATE(4294967295, -1)") == std::rotr(i, -1)); } } #endif @@ -2370,12 +2381,12 @@ TEST_CASE("Shift operators", "[shift]") { te_parser tep; - for (uint64_t i = 0; i < 63; ++i) + for (uint64_t i = 0; i < te_parser::get_max_integer_bitness()+1; ++i) { CHECK(tep.evaluate((std::string("1 << ") + std::to_string(i)).c_str()) == ((uint64_t)1 << i)); CHECK(tep.evaluate((std::string("1 >> ") + std::to_string(i)).c_str()) == ((uint64_t)1 >> i)); } - for (uint64_t i = 0; i < 62; ++i) + for (uint64_t i = 0; i < te_parser::get_max_integer_bitness()+1; ++i) { CHECK(tep.evaluate((std::string("2 << ") + std::to_string(i)).c_str()) == ((uint64_t)2 << i)); CHECK(tep.evaluate((std::string("2 >> ") + std::to_string(i)).c_str()) == ((uint64_t)2 >> i)); @@ -2401,17 +2412,14 @@ TEST_CASE("Shift operators", "[shift]") } SECTION("Left") { - CHECK_FALSE(tep.compile("1 << 64")); + CHECK_FALSE(tep.compile("1 << 54")); CHECK(std::isnan(tep.evaluate())); - CHECK(tep.get_last_error_message() == "Additive expression of left shift (<<) operation must be between 0-63."); CHECK(tep.evaluate("0 << 4") == ((uint64_t)0 << 4)); CHECK(std::isnan(tep.evaluate("1 << 64"))); CHECK(std::isnan(tep.evaluate("1 << -5"))); - CHECK(tep.get_last_error_message() == "Additive expression of left shift (<<) operation must be between 0-63."); - CHECK(tep.evaluate("31 << 59") == ((uint64_t)31 << 59)); + CHECK(tep.evaluate("31 << 53") == ((uint64_t)31 << 53)); // overflow CHECK(std::isnan(tep.evaluate("32 << 59"))); - CHECK(tep.get_last_error_message() == "Overflow in left shift (<<) operation; base number is too large."); CHECK(std::isnan(tep.evaluate("2 << 63"))); CHECK(std::isnan(tep.evaluate("-1 << 2"))); CHECK(tep.evaluate("1.0 << 4.0") == ((uint64_t)1 << 4)); @@ -2436,7 +2444,6 @@ TEST_CASE("Shift operators", "[shift]") CHECK(std::isnan(tep.evaluate("1 >> 64"))); CHECK(std::isnan(tep.get_result())); CHECK(std::isnan(tep.evaluate("1 >> -5"))); - CHECK(tep.get_last_error_message() == "Additive expression of right shift (>>) operation must be between 0-63."); CHECK(std::isnan(tep.get_result())); CHECK(tep.evaluate("32 >> 4") == ((uint64_t)32 >> 4)); CHECK(tep.evaluate("32 >> 5") == ((uint64_t)32 >> 5)); diff --git a/tinyexpr.cpp b/tinyexpr.cpp index e8900a0..0832673 100644 --- a/tinyexpr.cpp +++ b/tinyexpr.cpp @@ -446,6 +446,10 @@ namespace te_builtins { throw std::runtime_error("Bitwise RIGHT ROTATE value must be positive."); } + else if (val2 > 8) + { + throw std::runtime_error("Rotation operation must be between 0-8."); + } return static_cast(std::rotr(static_cast(val1), static_cast(val2))); } @@ -462,6 +466,10 @@ namespace te_builtins { throw std::runtime_error("Bitwise LEFT ROTATE value must be positive."); } + else if (val2 > 8) + { + throw std::runtime_error("Rotation operation must be between 0-8."); + } return static_cast(std::rotl(static_cast(val1), static_cast(val2))); } @@ -478,6 +486,10 @@ namespace te_builtins { throw std::runtime_error("Bitwise RIGHT ROTATE value must be positive."); } + else if (val2 > 16) + { + throw std::runtime_error("Rotation operation must be between 0-16."); + } return static_cast(std::rotr(static_cast(val1), static_cast(val2))); } @@ -494,6 +506,10 @@ namespace te_builtins { throw std::runtime_error("Bitwise LEFT ROTATE value must be positive."); } + else if (val2 > 16) + { + throw std::runtime_error("Rotation operation must be between 0-16."); + } return static_cast(std::rotl(static_cast(val1), static_cast(val2))); } @@ -510,6 +526,10 @@ namespace te_builtins { throw std::runtime_error("Bitwise RIGHT ROTATE value must be positive."); } + else if (val2 > 32) + { + throw std::runtime_error("Rotation operation must be between 0-32."); + } return static_cast(std::rotr(static_cast(val1), static_cast(val2))); } @@ -526,13 +546,17 @@ namespace te_builtins { throw std::runtime_error("Bitwise LEFT ROTATE value must be positive."); } + else if (val2 > 32) + { + throw std::runtime_error("Rotation operation must be between 0-32."); + } return static_cast(std::rotl(static_cast(val1), static_cast(val2))); } //-------------------------------------------------- [[nodiscard]] - static te_type te_right_rotate64(te_type val1, te_type val2) + static te_type te_right_rotate(te_type val1, te_type val2) { if (std::floor(val1) != val1 || std::floor(val2) != val2) { @@ -542,13 +566,18 @@ namespace te_builtins { throw std::runtime_error("Bitwise RIGHT ROTATE value must be positive."); } + else if (val2 > te_parser::get_max_integer_bitness()) + { + throw std::runtime_error("Rotation operation must be between 0-" + + std::to_string(te_parser::get_max_integer_bitness())); + } return static_cast(std::rotr(static_cast(val1), static_cast(val2))); } //-------------------------------------------------- [[nodiscard]] - static te_type te_left_rotate64(te_type val1, te_type val2) + static te_type te_left_rotate(te_type val1, te_type val2) { if (std::floor(val1) != val1 || std::floor(val2) != val2) { @@ -558,6 +587,11 @@ namespace te_builtins { throw std::runtime_error("Bitwise LEFT ROTATE value must be positive."); } + else if (val2 > te_parser::get_max_integer_bitness()) + { + throw std::runtime_error("Rotation operation must be between 0-" + + std::to_string(te_parser::get_max_integer_bitness())); + } return static_cast(std::rotl(static_cast(val1), static_cast(val2))); } @@ -577,6 +611,10 @@ namespace te_builtins { throw std::runtime_error("Bitwise OR operation must use positive integers."); } + else if (val1 > te_parser::MAX_BITOPS_VAL || val2 > te_parser::MAX_BITOPS_VAL) + { + throw std::runtime_error("Value is too large for bitwise operation."); + } return static_cast(static_cast(val1) | static_cast(val2)); } @@ -594,6 +632,10 @@ namespace te_builtins { throw std::runtime_error("Bitwise XOR operation must use positive integers."); } + else if (val1 > te_parser::MAX_BITOPS_VAL || val2 > te_parser::MAX_BITOPS_VAL) + { + throw std::runtime_error("Value is too large for bitwise operation."); + } return static_cast(static_cast(val1) ^ static_cast(val2)); } @@ -611,6 +653,10 @@ namespace te_builtins { throw std::runtime_error("Bitwise AND operation must use positive integers."); } + else if (val1 > te_parser::MAX_BITOPS_VAL || val2 > te_parser::MAX_BITOPS_VAL) + { + throw std::runtime_error("Value is too large for bitwise operation."); + } return static_cast(static_cast(val1) & static_cast(val2)); } @@ -619,27 +665,30 @@ namespace te_builtins [[nodiscard]] static te_type te_left_shift(te_type val1, te_type val2) { - constexpr int BITNESS_64BIT{ 64 }; // NOLINT - if (std::floor(val1) != val1) { throw std::runtime_error("Left side of left shift (<<) operation must be an integer."); } - if (std::floor(val2) != val2) + else if (std::floor(val2) != val2) { throw std::runtime_error( "Additive expression of left shift (<<) operation must be an integer."); } - if (val1 < 0) + else if (val1 < 0) { throw std::runtime_error("Left side of left shift (<<) operation cannot be negative."); } - // bitness is limited to 64-bit, so ensure shift doesn't go beyond that + else if (val1 > te_parser::MAX_BITOPS_VAL) + { + throw std::runtime_error("Value is too large for bitwise operation."); + } + // bitness is limited to 53-bit, so ensure shift doesn't go beyond that // and cause undefined behavior - if (val2 < 0 || val2 >= BITNESS_64BIT) + else if (val2 < 0 || val2 > te_parser::get_max_integer_bitness()) { throw std::runtime_error( - "Additive expression of left shift (<<) operation must be between 0-63."); + "Additive expression of left shift (<<) operation must be between 0-" + + std::to_string(te_parser::get_max_integer_bitness())); } const auto multipler = (static_cast(1) << static_cast(val2)); @@ -656,25 +705,28 @@ namespace te_builtins [[nodiscard]] static te_type te_right_shift(te_type val1, te_type val2) { - constexpr int BITNESS_64BIT{ 64 }; // NOLINT - if (std::floor(val1) != val1) { throw std::runtime_error("Left side of right shift (>>) operation must be an integer."); } - if (std::floor(val2) != val2) + else if (std::floor(val2) != val2) { throw std::runtime_error( - "Additive expression of right shift (>>)operation must be an integer."); + "Additive expression of right shift (>>) operation must be an integer."); } - if (val1 < 0) + else if (val1 < 0) { throw std::runtime_error("Left side of right shift (<<) operation cannot be negative."); } - if (val2 < 0 || val2 >= BITNESS_64BIT) + else if (val1 > te_parser::MAX_BITOPS_VAL) + { + throw std::runtime_error("Value is too large for bitwise operation."); + } + else if (val2 < 0 || val2 > te_parser::get_max_integer_bitness()) { throw std::runtime_error( - "Additive expression of right shift (>>) operation must be between 0-63."); + "Additive expression of right shift (>>) operation must be between 0-" + + std::to_string(te_parser::get_max_integer_bitness())); } return static_cast(static_cast(val1) >> static_cast(val2)); @@ -934,8 +986,8 @@ const std::set te_parser::m_functions = { // NOLINT { "bitrrotate16", static_cast(te_builtins::te_right_rotate16), TE_PURE }, { "bitlrotate32", static_cast(te_builtins::te_left_rotate32), TE_PURE }, { "bitrrotate32", static_cast(te_builtins::te_right_rotate32), TE_PURE }, - { "bitlrotate64", static_cast(te_builtins::te_left_rotate64), TE_PURE }, - { "bitrrotate64", static_cast(te_builtins::te_right_rotate64), TE_PURE }, + { "bitlrotate", static_cast(te_builtins::te_left_rotate), TE_PURE }, + { "bitrrotate", static_cast(te_builtins::te_right_rotate), TE_PURE }, #endif { "bitlshift", static_cast(te_builtins::te_left_shift_or_right), TE_PURE }, { "bitrshift", static_cast(te_builtins::te_right_shift_or_left), TE_PURE }, @@ -1234,14 +1286,14 @@ void te_parser::next_token(te_parser::state* theState) (*std::next(theState->m_next) == '<')) { theState->m_type = te_parser::state::token_type::TOK_INFIX; - theState->m_value = static_cast(te_builtins::te_left_rotate64); + theState->m_value = static_cast(te_builtins::te_left_rotate); std::advance(theState->m_next, 2); } else if (tok == '>' && (*theState->m_next == '>') && (*std::next(theState->m_next) == '>')) { theState->m_type = te_parser::state::token_type::TOK_INFIX; - theState->m_value = static_cast(te_builtins::te_right_rotate64); + theState->m_value = static_cast(te_builtins::te_right_rotate); std::advance(theState->m_next, 2); } #else @@ -1665,8 +1717,8 @@ te_expr* te_parser::expr_level8(te_parser::state* theState) get_function2(theState->m_value) == te_builtins::te_right_shift #if __cplusplus >= 202002L && !defined(TE_FLOAT) || - get_function2(theState->m_value) == te_builtins::te_left_rotate64 || - get_function2(theState->m_value) == te_builtins::te_right_rotate64 || + get_function2(theState->m_value) == te_builtins::te_left_rotate || + get_function2(theState->m_value) == te_builtins::te_right_rotate || get_function2(theState->m_value) == te_builtins::te_left_rotate32 || get_function2(theState->m_value) == te_builtins::te_right_rotate32 || get_function2(theState->m_value) == te_builtins::te_left_rotate16 || diff --git a/tinyexpr.h b/tinyexpr.h index 862f987..8b62c08 100644 --- a/tinyexpr.h +++ b/tinyexpr.h @@ -257,6 +257,9 @@ class te_parser /// @brief No position, which is what get_last_error_position() returns /// when there was no parsing error. constexpr static int64_t npos = -1; + /// @private + // (2^48)-1 + constexpr static double MAX_BITOPS_VAL{ 281474976710655 }; // NOLINT /// @returns @c true if the parser's internal type can hold `uint32_t` without truncation. [[nodiscard]] constexpr static bool supports_32bit() noexcept @@ -269,6 +272,13 @@ class te_parser { return std::numeric_limits::digits >= std::numeric_limits::digits; } + /// @returns The bits available in the internal data type.\n + /// This will affect the largest integer size that can be used in bitwise operations. + [[nodiscard]] + constexpr static int get_max_integer_bitness() noexcept + { + return std::numeric_limits::digits; + } /// @returns The largest integer value that the parser can handle without truncation. [[nodiscard]] static te_type get_max_integer()