Shop OBEX P1 Docs P2 Docs Learn Events
Log/Antilog Table In C/C++ — Parallax Forums

Log/Antilog Table In C/C++

DavidZemonDavidZemon Posts: 2,973
edited 2015-03-03 18:36 in Propeller 1
I'm reading through the Propeller manual and trying to understand the log & antilog tables, but I'm just not getting it. Has anyone done this in C/C++? The Spin syntax is definitely throwing me off a bit - it's just been too long since I've used Spin.

I'd like to add methods for the log and antilog to my PropWare::Utility class.

Comments

  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-02-28 08:35
    I did find this thread and looked at the source code. Still in spin. Still couldn't quite grep it :(
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-03-01 06:07
    I'm fine with math too.... if anyone can just show me the formulas, I can take it from there. I think it's just the terminology used in the manual doesn't quite click in my head.
  • ersmithersmith Posts: 6,053
    edited 2015-03-02 10:49
    When I need log or antilog I usually just use the log and exp functions from libm.a.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-03-02 16:31
    ersmith wrote: »
    When I need log or antilog I usually just use the log and exp functions from libm.a.

    Adding this line "unsigned int x = log2(8);" used an extra ~500 bytes. That's no fun :(.

    My current use case is replacing the following snippet of code:
    switch (sectorsPerCluster) {
        case 1:
            this->m_sectorsPerCluster_shift = 0;
            break;
        case 2:
            this->m_sectorsPerCluster_shift = 1;
            break;
        case 4:
            this->m_sectorsPerCluster_shift = 2;
            break;
        case 8:
            this->m_sectorsPerCluster_shift = 3;
            break;
        case 16:
            this->m_sectorsPerCluster_shift = 4;
            break;
        case 32:
            this->m_sectorsPerCluster_shift = 5;
            break;
        case 64:
            this->m_sectorsPerCluster_shift = 6;
            break;
        case 128:
            this->m_sectorsPerCluster_shift = 7;
            break;
        default:
            return BAD_SECTORS_PER_CLUSTER;
    }
    

    Surely I can save a few bytes of code by switching to the built-in log table, all the while providing extra functionality to PropWare users.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-03-02 16:33
    Unless the entire reason I'm so very confused stems from my assumption that the log table uses integers or fixed point decimals.... I didn't consider until now that the log tables require use of floats :(
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-03-02 18:47
    The problem with using the log table is that is only handles the range from 0.5 to 0.999, or more precisely from 4096/8192 to 8190/8192. What you want to do is handle a range from 1 to 128, which requires determining the position of the 1 bit. I think using a switch statement is probably about as fast and compact as you can go.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-03-02 18:55
    Well that certainly explains why I was having so much trouble grasping the Spin code.

    I'm with you on the switch case being faster/smaller.

    Just to clarify: are you confirming that the tables in ROM use floating point (as in, IEEE 754) numbers? And, if that is the case, the math functions in libm.a probably use those table already, right? And that would be why no one has written their own code to do this in C/C++ - because it would be exactly the same code in PropGCC?
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-03-03 00:10
    The ROM log table is fixed point, and not floating point. The example in the Prop manual shows that the number must first be normalized before it can be used as an index into the log table. The number is shifted to the left until the first non-zero bit is in bit position 31. The exponent is then 31 minus the number of shifts, which is just the original bit position of the leading non-zero bit. The fractional part of the log is obtained by using the bits to the right of the leading non-zero bit as an index into the lookup table. I believe this can be done by shifting the normalized number down by 19 bit positions, and then adding 0xb0000000. The log value is then obtained by looking up the 16-bit entry in the log table, and merging it with the exponent in bits 16-20. The resulting number has an implied binary point between bits 16 and 15.
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-03-03 10:10
    Just for fun I coded up a C program that uses the ROM log table. It seems to be accurate to between 3 and 4 decimal digits, which is consistent with using a table with 2048 entries.
    #include <stdio.h>
    #include <stdlib.h>
    #include <propeller.h>
    
    int rom_log(int x)
    {
        int exp;
        unsigned short *ptr;
        
        if (!x) return 0;
        
        for (exp = 31; x > 0; exp--) x <<= 1;
        ptr = (unsigned short *)((((unsigned int)x) >> 19) + 0xb000);
        return (exp << 16) | *ptr;
    }   
        
    int main()
    {
        int val;
        double x, y;
        
        waitcnt(CNT+20000000);
        while(1)
        {
            printf("Enter value: ");
            scanf("%d", &val);
            x = (double)rom_log(val)/65536.0;
            y = pow(2.0, x);
            printf("%d %f %f\n", val, x, y);
        }    
        return 0;
    }
    
  • DavidZemonDavidZemon Posts: 2,973
    edited 2015-03-03 18:36
    Dave Hein wrote: »
    Just for fun I coded up a C program that uses the ROM log table. It seems to be accurate to between 3 and 4 decimal digits, which is consistent with using a table with 2048 entries.

    you rock :) Thank you!

    David
Sign In or Register to comment.