Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

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;
}
 
There is a strange quirk with XC8... Their "internal promotion" requires forethought .

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

4.6.1 Integral Promotion
Integral promotion is always applied in accordance with the C standard, but it can confuse those who are not expecting such behavior.
When there is more than one operand to an operator, they typically must be of exactly
the same type. The compiler will automatically convert the operands, if necessary, so
they do have the same type. The conversion is to a “larger” type so there is no loss of
information; however, the change in type can cause different code behavior to what is
sometimes expected. These form the standard type conversions.
Prior to these type conversions, some operands are unconditionally converted to a
larger type, even if both operands to an operator have the same type. This conversion
is called integral promotion. The compiler performs these integral promotions where
required, and there are no options that can control or disable this operation.
Integral promotion is the implicit conversion of enumerated types, signed or
unsigned varieties of char, short int or bit-field types to either signed int or
unsigned int. If the result of the conversion can be represented by an signed int,
then that is the destination type, otherwise the conversion is to unsigned int.

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.
 
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??
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;
 
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;
        }
    }
}

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?.
 
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;
        }
    }
}

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

Thanks again :D
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top