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()