Modulus (%) operator in XC8

Status
Not open for further replies.

Nigel Goodwin

Super Moderator
Most Helpful Member
Does anyone know if the modulus operator is 16 bit only?, I've been trying to extend the following routine to make it 24 bit (or 32) rather than just 16 bit.

C:
unsigned char TenK, Thou, Hund, Tens, Ones;    // Decimal convert routine variables

void convert16(unsigned int numb)                  // Takes an unsigned integer number
{                                                                          // and converts to single
    TenK = numb / 10000;                                   // decimal digits.
    Thou = (numb % 10000) / 1000;
    Hund = (numb % 1000) / 100;
    Tens = (numb % 100) / 10;
    Ones = numb % 10;
}

But I've not managed to make it work, nor find any information about how many bits it will work with - I'm presuming it's down to only 16 bits?, as it fails above 65535, once a 17th bit appears the most significant digit vanishes.

The routines are fast, small, and allow you to display the digits in anyway you like, particularly useful for adding decimal points where you want them.

I've managed to get sprintf to create a string from 24 bits, but it's a lot slower and more resource heavy - and I prefer the separate variables rather than a string array.
 
I have my own "printFloat" routine that takes a long and makes it a fixed point string. The largest % is 100,000 and it works okay BUT!!!!!! this was compiled under C18 and not XC8

Its here
C:
void printFloat(char* flt, long number, char digits, char zero)
    {
    char sign = 0x20, sign1 = 0x20;
    char blank = 1;
    if(zero) sign1 = 0x30;

    if(number < 0)
        {
        number = abs(number);
        sign = 0x2d;
        }
    if(digits == 2)
        {
        flt[0] = sign; 
        flt[1] = (( number % 10000) / 1000) + 48;
        if(flt[1] == 48 && blank) 
            flt[1] = sign1;
        else
            blank = 0;
        flt[2] = ((number % 1000) / 100)+ 48;
        flt[3] = 46;
        flt[4] = ((number % 100) / 10)+ 48;
        flt[5] = (number % 10) + 48;
        flt[6] = 0;
        }
    else if(digits == 1)
        {
        flt[0] = sign; 
        flt[1] = (( number % 10000) / 1000) + 48;
        if(flt[1] == 48 && blank) 
            flt[1] = sign1;
        else
            blank = 0;
        flt[2] = ((number % 1000) / 100)+ 48;
        if(flt[2] == 48 && blank) 
            flt[2] = sign1;
        else
            blank = 0;
        flt[3] = ((number % 100) / 10)+ 48;
        flt[4] = 46;
        flt[5] = (number % 10) + 48;
        flt[6] = 0;
        }
    else
        {
        flt[0] = sign; 
        flt[1] = (( number % 100000) / 10000) + 48;
        if(flt[1] == 48 && blank) 
            flt[1] = sign1;
        else
            blank = 0;
        flt[2] = (( number % 10000) / 1000) + 48;
        if(flt[2] == 48 && blank) 
            flt[2] = sign1;
        else
            blank = 0;
        flt[3] = ((number % 1000) / 100)+ 48;
        if(flt[3] == 48 && blank) flt[3] = 0x20;
        flt[4] = ((number % 100) / 10)+ 48;
        flt[5] = (number % 10) + 48;
        flt[6] = 0;
        }
    }

I only have the three requirements
 
You're passing convert16 an unsigned int, which is 16 bits.
Try passing it an unsigned long instead. That should work to 32 bits.
 
You're passing convert16 an unsigned int, which is 16 bits.
Try passing it an unsigned long instead. That should work to 32 bits.
No, that was just the original working 16 bit routine, the 24 (and 32) bit routines passed the correct variables, as below - neither will go above 65535.

C:
void convert24(uint24_t numb)                   // Takes a unsigned short long (24 bit) number
{                                               // and converts to single
    TenM =  numb / 10000000;                    // decimal numbers in unsigned char variables.
    Mill = (numb % 10000000) / 1000000;   
    HunK = (numb % 1000000) / 100000;
    TenK = (numb % 100000) / 10000;           
    Thou = (numb % 10000) / 1000;
    Hund = (numb % 1000) / 100;
    Tens = (numb % 100) / 10;
    Ones =  numb % 10;
}

void convert32(uint32_t numb)                   // Takes a unsigned long (32 bit) number
{                                               // and converts to single
    Bill =  numb / 1000000000;                  // decimal numbers.
    HunM = (numb % 1000000000) / 100000000;
    TenM = (numb % 100000000) / 10000000;               
    Mill = (numb % 10000000) / 1000000;   
    HunK = (numb % 1000000) / 100000;
    TenK = (numb % 100000) / 10000;           
    Thou = (numb % 10000) / 1000;
    Hund = (numb % 1000) / 100;
    Tens = (numb % 100) / 10;
    Ones = numb % 10;
}
 
I have my own "printFloat" routine that takes a long and makes it a fixed point string. The largest % is 100,000 and it works okay BUT!!!!!! this was compiled under C18 and not XC8
I wonder if C18 was different?. I'll have to have a play with it
 
There is a strange quirk with XC8... Their "internal promotion" requires forethought .

It defaults to "unsigned int"!! Here be that passage


Looks like you need to cast a bit!! Since I moved from C18 I have have many of these popping up..
Looks like I was blind to this... If I port to XC32 I always get promotional issues.
 
The issue may lay with the constants.. Try the postfix L to denote a long

HunM = (numb % 1000000000L) / 100000000L;
 
Just tried it:

Code:
0082533 uSecs
982533 uSecs

Top line is using Convert24, bottom line is using sprintf - I notice the failed number (in this case) exceeds 65535.

C:
void convert24(uint24_t numb)                   // Takes a unsigned short long (24 bit) number
{                                               // and converts to single
    TenM =  numb / 10000000L;                    // decimal numbers in unsigned char variables.
    Mill = (numb % 10000000L) / 1000000L;   
    HunK = (numb % 1000000L) / 100000L;
    TenK = (numb % 100000L) / 10000L;           
    Thou = (numb % 10000) / 1000;
    Hund = (numb % 1000) / 100;
    Tens = (numb % 100) / 10;
    Ones =  numb % 10;
}
 
Something else is wrong there as the top number IS above 65535 by nearly 20K
The HunK isn't getting though. AND the TenM isn't providing an output..

What function do you use to convert to ascii??
 
Something else is wrong there as the top number IS above 65535 by nearly 20K
The HunK isn't getting though. AND the TenM isn't providing an output..

What function do you use to convert to ascii??
A simple and fast one, which allows me to easily sneak decimal points in where I want.

C:
void Ser_Unsigned24(uint24_t value)
{
    convert24(value);
    buffer[0] = TenM+=0x30;
    buffer[1] = Mill+=0x30;
    buffer[2] = TenK+=0x30;
    buffer[3] = Thou+=0x30;
    buffer[4] = Hund+=0x30;
    buffer[5] = Tens+=0x30;
    buffer[6] = Ones+=0x30;
    buffer[7] = 0x00;
    Ser_printR(buffer);
}

Like this:

C:
void Ser_Unsigned24(uint24_t value)
{
    convert24(value);
    buffer[0] = TenM+=0x30;
    buffer[1] = Mill+=0x30;
    buffer[2] = TenK+=0x30;
    buffer[3] = Thou+=0x30;
    buffer[4] = Hund+=0x30;
    buffer[5] = '.';
    buffer[6] = Tens+=0x30;
    buffer[7] = Ones+=0x30;
    buffer[8] = 0x00;
    Ser_printR(buffer);
}

The function Ser_printR(buffer) simply sends the string out the serial port
 
Is there any reason you don't just use modulus 10 for all calculations? It should be faster and use less code.

The following uses a loop to make it more flexible, and modulus of 10 to make it faster:
C++:
void convert(uint32_t val, int digits, int decimalPointPosition, char* buff)
{
    if (decimalPointPosition < digits)
    {
        digits++;
    }

    buff[digits] = '\0';
    while (digits--)
    {
        if (digits == decimalPointPosition)
        {
            buff[digits] = '.';
        }
        else
        {
            buff[digits] = (char)(val % 10) + '0';
            val /= 10;
        }
    }
}

There's a typo in your code here, where the digit value is assigned a new value.
buffer[0] = TenM+=0x30;
 

Just that I've long used the previous routine, and I like the separate (non-array) variables it provides. Currently I'm using sprintf to convert to a string, which is working fine - and OK on a high spec PIC, but resource hungery on a small one. As I'm using an 18F27K42 it's not an issue, with 128K of program memory and 8K of RAM.

I'll have a play with your routine, and see how I get on - thanks very much.

There's a typo in your code here, where the digit value is assigned a new value.

I'm still missing it? - what's the typo?.
 

Just to let you know, tried your routine, and it works fine.

Thanks again
 
At home.... Just put your convert24 routine to test.. Works fine... SOOOOO I think the ASCII convertion was at fault... Using XC8 V1.44 ( I know its an oldy but I have the Pro version..)
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…