Jump to content
43oh

2^N for real numbers using integer math


Recommended Posts

When working with electronic music (MIDI synth and such) it is sometimes necessary to adjust pitch by units of octaves, semitones, or cents.

 

An octave is a 2:1 ratio. So the ratio between any number of octaves is 2^N. This is a simple bit shift. Easy.

 

A semitone is 1/12 of an octave (typically), so the ratio for that is 2^(N/12). A cent is 1/1200 of an octave, so the ratio for that is 2^(N/1200). Can't use bit shifting for that!

 

The C stdlib provides the pow() function that can be used for these calculations, but it uses rather slow floating point math.

 

This code will do it with relatively fast fixed point integer math. This code could easily be adapted to almost any base and exponent by changing the look-up tables. It uses the property of exponents that 2 ^ (1 + 2 + 4 + 8) == (2 ^ 1) * (2 ^ 2) * (2 ^ 4) * (2 ^ 8)

 

 

//
// 2 ^ (n / (1200 * 8192))
//
// n = 1 / 8,192 cent
//   = 1 / 819,200 semitone
//   = 1 / 9,830,400 octave
//
// range of n: -134,217,727 to +78,643,199
//            = -13.65 to +7.99 octaves
//
// returned value is 8.24 fixed point
//
// + exponents in 8.24 format
static const uint32_t etp[27] = {
    0x01000001,                     // 00000001  1.0000000705
    0x01000002,                     // 00000002  1.0000001410
    0x01000005,                     // 00000004  1.0000002820
    0x01000009,                     // 00000008  1.0000005641
    0x01000013,                     // 00000010  1.0000011282
    0x01000026,                     // 00000020  1.0000022563
    0x0100004C,                     // 00000040  1.0000045127
    0x01000097,                     // 00000080  1.0000090254
    0x0100012F,                     // 00000100  1.0000180509
    0x0100025E,                     // 00000200  1.0000361021
    0x010004BB,                     // 00000400  1.0000722054
    0x01000977,                     // 00000800  1.0001444161
    0x010012EE,                     // 00001000  1.0002888530
    0x010025DE,                     // 00002000  1.0005777895
    0x01004BC1,                     // 00004000  1.0011559129
    0x01009798,                     // 00008000  1.0023131618
    0x01012F8B,                     // 00010000  1.0046316744
    0x0102607D,                     // 00020000  1.0092848012
    0x0104C6A1,                     // 00040000  1.0186558100
    0x0109A410,                     // 00080000  1.0376596592
    0x0113A513,                     // 00100000  1.0767375682
    0x0128CC11,                     // 00200000  1.1593637909
    0x01581889,                     // 00400000  1.3441243996
    0x01CE81F4,                     // 00800000  1.8066704016
    0x0343994D,                     // 01000000  3.2640579400
    0x0AA77169,                     // 02000000  10.6540742354
    0x71826157                      // 04000000  113.5092978129
};
// - exponents in 8.24 format
static const uint32_t etn[27] = {
    0x00FFFFFF,                     // 00000001  0.9999999295
    0x00FFFFFE,                     // 00000002  0.9999998590
    0x00FFFFFB,                     // 00000004  0.9999997180
    0x00FFFFF7,                     // 00000008  0.9999994359
    0x00FFFFED,                     // 00000010  0.9999988718
    0x00FFFFDA,                     // 00000020  0.9999977437
    0x00FFFFB4,                     // 00000040  0.9999954873
    0x00FFFF69,                     // 00000080  0.9999909747
    0x00FFFED1,                     // 00000100  0.9999819495
    0x00FFFDA2,                     // 00000200  0.9999638992
    0x00FFFB45,                     // 00000400  0.9999277998
    0x00FFF689,                     // 00000800  0.9998556048
    0x00FFED13,                     // 00001000  0.9997112304
    0x00FFDA28,                     // 00002000  0.9994225441
    0x00FFB455,                     // 00004000  0.9988454217
    0x00FF68C1,                     // 00008000  0.9976921765
    0x00FED1DC,                     // 00010000  0.9953896791
    0x00FDA51C,                     // 00020000  0.9908006133
    0x00FB4FC4,                     // 00040000  0.9816858552
    0x00F6B582,                     // 00080000  0.9637071184
    0x00EDC157,                     // 00100000  0.9287314100
    0x00DCCF8E,                     // 00200000  0.8625420320
    0x00BE7564,                     // 00400000  0.7439787570
    0x008DB277,                     // 00800000  0.5535043908
    0x004E6E13,                     // 01000000  0.3063671106
    0x00180743,                     // 02000000  0.0938608065
    0x0002415D                      // 04000000  0.0088098510
};
uint32_t tpow(int32_t n)
{
    const uint32_t *et = etp;           // Assume + exponent
    if(n < 0) n = -n, et = etn;         // Adjust for - exponent
                                        //
    uint32_t r = 1L << 24;              // Init result to 1.0 in 8.24 format
                                        //
    do {                                //
        if(n & 1) r = mul824(r, *et);   // Multiply by exponent if lsb of n is 1
        ++et;                           // Next exponent
    } while(n >>= 1);                   // Next bit, loop until no more 1 bits
                                        //
    return r;                           // Return result
}
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...