The encoding in terms of prime exponents is known as the Godel encoding, and its a fairly important step in proving things like Godel's incompleteness theorems and the undecidability of the Halting problem.
Essentially it's a one-to-one map between natural numbers and finite sequences of natural numbers (since you can encode (a,b,c,d...) as 2^a × 3^b × 5^c × 7^d etc), which turns out to be mathematically a convenient thing to have.
Classic examples include:
- time domain / frequency domain
- lookup table / functional form
- traditional binary representation / gray codes
A × B = C
then log A + log B = log C
And if D^E = F
then E × log D = log F
Mostly a tongue in cheek comment but if you don't know log rules at all check out log tables [0]. And you'll see that if you use log values then multiplication and division become addition and subtraction and equally simply.Relies on you either having stored the needed data or efficiently calculating logirithms and antilogirithims.
For non-negative values, the exponent (upper) bits give the integer part of the logarithm and the significand (lower) bits approximate the fractional part (i.e., the mantissa).
In other words, you can do:
float approxLog2( float value )
{
assert( value > 0.0f );
auto bits = std::bit_cast< int32_t >( value );
auto exponent = ( bits >> 23 ) - 127;
auto significand = bits & ( ( 1 << 23 ) - 1 );
return exponent + significand / static_cast< float >( 1 << 23 );
}
This will be exact at powers of two, and within 0.0860748291 of the true value in between (always being slightly too low).The above is also not true of factors of the modulo itself but then that's just a right shift so that part is easy.
eg. Under mod 35 if i want to divide by 3 i can just multiply by 12 since 12x3 = 1 under mod 35.
eg2. Under mod 35 if i want to divide by 11 i can just multiply by 16 since 16x11 = 1 under mod 35.
I can do this for any number that doesn't share factors with 35 since there's always a multiple to that number that will give 1. I could also create base 2 examples similarly. It's called the multiplicative inverse.
Modular division is straightforward to convert to a multiplication; however this is mainly for specialized applications, like cryptography and number theory. It's uncommon to want divide-by-2 to make the value larger.
Ordinary flooring division can also be converted to a multiplication problem when the dividend is bounded. This uses the "magic number" approach where you multiply by a rounded scaled reciprocal, and then do some work to correct the error from rounding.
>It's worth noting that you can generally divide much faster if you know the divisor ahead of time
Division is just multiplication with 1/x so i don't understand
It gets tricky because 2^32/3 is not an integer, so you must round it. This introduces error; the idea is to show that the error is wiped out by the flooring divide. I did the monster math here:
https://ridiculousfish.com/blog/posts/labor-of-division-epis...
Also, with integers, a signed right shift is rounding down (towards negative infinity), whereas the division operator/instruction in many languages/hardware is rounding towards 0.
To adjust the rounding, you'd add the sign-bit to the first fractional bit before shifting the last step. Let's say that 'x' is a signed long, and a signed long has 64 bits, then:
result = ((x >> amount-1) + ((unsigned long)x >> 63)) >> 1;
Yes, but this was historically okay on an x87 FPU which had more precise representation than the common external formats.
Here's an example: https://godbolt.org/z/8vG637fb5
It compiles x/6 to some mad multiplication, bitshift and add.
For example, if you want to normalize a 3D vector you could do:
mag = sqrt(x*x + y*y + z*z)
x /= mag
y /= mag
z /= mag
That's three divisions with the same divisor. But you could instead do: invMag = 1.0 / sqrt(x*x + y*y + z*z)
x *= invMag
y *= invMag
z *= invMag
There's still a single division (or reciprocal) done here. But you've eliminated at least the other two. (And it's even better if you have an rsqrt function.)