// SPDX-License-Identifier: MIT // Mathematical operators that don't reuse C++'s behavior #include "opmath.hpp" #include #include "helpers.hpp" // clz, ctz int32_t op_divide(int32_t dividend, int32_t divisor) { // Adjust division to floor toward negative infinity, // not truncate toward zero int32_t remainder = dividend % divisor; return dividend / divisor - (remainder != 0 && (remainder < 0) != (divisor < 0)); } int32_t op_modulo(int32_t dividend, int32_t divisor) { // Adjust modulo to have the sign of the divisor, // not the sign of the dividend return static_cast( dividend - static_cast(op_divide(dividend, divisor)) * divisor ); } int32_t op_exponent(int32_t base, uint32_t power) { uint32_t result = 1; for (uint32_t ubase = base; power; power /= 2) { if (power % 2) { result = static_cast(result) * ubase; } ubase = static_cast(ubase) * ubase; } // Avoid arithmetic overflow runtime error return result <= INT32_MAX ? result : -static_cast(~result) - 1; } int32_t op_shift_left(int32_t value, int32_t amount) { // Get the easy cases out of the way if (amount == 0) { return value; } if (value == 0 || amount >= 32) { return 0; } if (amount < -31) { return (value < 0) ? -1 : 0; } if (amount < 0) { return op_shift_right(value, -amount); } // Use unsigned to force a bitwise shift // Casting back is OK because the types implement two's complement behavior return static_cast(value) << amount; } int32_t op_shift_right(int32_t value, int32_t amount) { // Repeat the easy cases here to avoid INT_MIN funny business if (amount == 0) { return value; } if (value == 0 || amount < -31) { return 0; } if (amount > 31) { return (value < 0) ? -1 : 0; } if (amount < 0) { return op_shift_left(value, -amount); } if (value > 0) { return static_cast(value) >> amount; } // Calculate an OR mask for sign extension // 1->0x80000000, 2->0xC0000000, ..., 31->0xFFFFFFFE uint32_t amount_high_bits = -(UINT32_C(1) << (32 - amount)); // The C++ standard leaves shifting right negative values // undefined, so use a left shift manually sign-extended return (static_cast(value) >> amount) | amount_high_bits; } int32_t op_shift_right_unsigned(int32_t value, int32_t amount) { // Repeat the easy cases here to avoid INT_MIN funny business if (amount == 0) { return value; } if (value == 0 || amount < -31 || amount > 31) { return 0; } if (amount < 0) { return op_shift_left(value, -amount); } return static_cast(value) >> amount; } int32_t op_neg(int32_t value) { // Avoid negating signed INT_MIN return static_cast(-static_cast(value)); } int32_t op_high(int32_t value) { return static_cast(static_cast(value) >> 8 & 0xFF); } int32_t op_low(int32_t value) { return value & 0xFF; } int32_t op_bitwidth(int32_t value) { return value != 0 ? 32 - clz(static_cast(value)) : 0; } int32_t op_tzcount(int32_t value) { return value != 0 ? ctz(static_cast(value)) : 32; }