This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Integer truncation & float division

Guru 56033 points
Part Number: EK-TM4C1294XL


Tool/software: Code Composer Studio

Seemingly long integers are not correctly converted to decimal are being truncated inline via long to short conversion during base 10 division prior to ltoa() conversion to 8bit strings. The other oddity occurs during floating point division. Large values in the 1's position are left shifted 1 binary position, so 7 becomes 14 and the 10ths position is always 0.9. So 12.2 becomes 12.9 but 18.9 remains 18.9 after integer division via (float) declaration front of int32MOSTempValueHigh shown below. This issues occurs across several long variable names not just 1 variable being ported to serial UART transport.

Why does the int32 not correctly convert to floating values 0.1-0.9 when the inline input division occurs? Note the NexSendCmd() sets the Xfloat widget for the correct static decimal place as they are not dynamic floating decimal spinners like GUI composer. However it too has issues in the same floating division as occurs in the 1st example below, int32 << 1 occurs.  

Why are the seconds count not properly creating characters 0-9 in the 1's place as seconds count rolls from 99 to 101, is always 104, 294, 304 etc.? Yet the seconds count 0-9 is converted and divides without character truncation (0-99) yet 1910 is output as 1949 seconds remains until Watch Element count (1999 rolls 2000) 1949 then becomes 2050. How is the division shown below struggling to divide turtle slow seconds counter? Have I made an integer division mistake or does the compiler not correctly convert long to short in this case integer truncation occurs?

	// Set Xfloat decimal point 10's place 
if((float)int32MOSTempValueHigh / 10 < 1000) { while(!(NexSendCmd("Hmostmpx.vvs0=2",0,0,0))); while(!(NexSendCmd("Hmostmpx.vvs1=1",0,0,0))); while(!(NexSendCmd("Hmostmpx.val=", (float)int32MOSTempValueHigh / 10, 0, 0))); }

  • This line looks wrong.

    BP101 said:
        while(!(NexSendCmd("Hmostmpx.val=", (float)int32MOSTempValueHigh / 10, 0, 0)));

    Focus on the 2nd argument to the function NexSendCmd.  This is the order of computation.

    1. The integer variable is converted to float
    2. The float expression is divided by 10

    This also means the second argument to NexSendCmd is of type float, and not uint16_t.

    Thanks and regards,

    -George

  • Hi George,

    The conversion results are the same with or without float. A bigger issue is how the compiler 18.12.2.LTS incorrectly handles (sizeof) operator. It is also not returning full strings (char) from array types back to the calling function in the attached example. Note the attached example UARTprintf() reports the ltoa() input integers converted to a string but returns only one char to the caller no matter how big the array on either side. This example uses UART0 to produse debug output of how integers are being divided into bazar values at times depending on the size. For the most past 16 bits of uint32_t handles most all variable integers in the primary application having said issues.

    CCS Debug breaks will show full integer return string spans several array cells via return() when made >[0]. Yet the function char returns but 1 char no matter if while loop was added to ltoa() or itoa(). Note too itoa() can be switched into the loop and similar results, just REM ltoa(). The (sizeof) operator is incorrectly returning type (uint32_t) 4 bytes and not the length of the actual bytes in (uint32_t) conversion to (uint16_t) variable as it should be doing. We don't need to know the defined type size of variable, rather a byte count of the real time variable is required.

  • I'm confused about ...

    BP101 said:
    The (sizeof) operator is incorrectly returning type (uint32_t) 4 bytes

    The sizeof(uint32_t) is 4.  What do you expect instead?

    Thanks and regards,

    -George

  • George Mock said:
    The sizeof(uint32_t) is 4.  What do you expect instead?

    How about the actual byte length of the integers inside a uint32_t and not the size of what is plainly evident. I think it may be doing the first part and I was confused since a uint32_t conversion to uint16_t with one byte length is always indicating 1 in CCS debug.

    CCS debug stepping through ltoa() or itoa() with converted variable integers both produce incorrect string conversions. So a decimal 3 ends up being blank (nothing) and decimal 50 becomes 5053 when ported to UART as character for ltoa() or itoa(). Porting integers (int) directly to the UART FIFO causes rolling POR conditions. Oddly ltoa() or itoa() work well for (short) integers constringed to 2 byte text box but not for longer uint32_t converted to uint16_t. With the same uint32_t converted bytes (sizeof) appears to split the uint16_t in half as 1 byte length for each array, output[0] and output[1] but the values are wrong in that case.

    Oddly the same UART command decoder also ports 2 byte (short) integers into GUI text boxes and those bytes are spot on value. Any integer longer than 2 bytes in the same decoder seems to cause issue for ltoa() and itoa() producing wrong values. It seems ltoa() return often produces negative values from it's buffer, adding 0x50 to the converted value rather than 0x30. So the blank decimal 3 ends up as 53 in the next output array cell.

    The question and for the example application is why?   

  • I'm not sure what you mean by ...

    BP101 said:
    How about the actual byte length of the integers inside a uint32_t and not the size of what is plainly evident.

    Please supply an detailed example illustrates this behavior.  Show what result sizeof gives you, and the result you expect instead.

    Thanks and regards,

    -George

  • Again the example is the attached program above post. It shows the results of (sizeof) failure to produce a correct byte count of the passed integers into the called header via several variables. Sizeof is not to report the typedef known integer size, rather to report the number of bytes passed into the header of the called function which is never obvious and very dynamic. 

    The other biggest obstacle to passed header integers; strlen() adds '\0' NULL to passed C+ header integers and ltoa() oddly removes the NULL. That causes random array pointer failures via register PUSH/POP with incremented integer pointers inside for or while loops.

    Oddly the for loop and integer pointer into a passed in header integer to an buffer array and output via UART is often incorrect using (sizeof) to determine the passed in integers size to a buffer array single cell. Similarly for strlen() the same issue occurs unless the not so obvious NULL character '\0' is actually tested in that same for or while transmit character loop. Where the for loop produces incorrect integer conversion results the while test for '\0' also causes array pointer issues and application must weight each passed in integer to the handler to achieve correct UART transmitted integers that MIRROR the original variable contents up to and including uint16_t or unsigned short .

    	case 1:/* Variable integers convert to strings */
    	{
                    bTxDone = false;
    			
    		/* Convert integers to nulled strings */
    		itoa(varin, varout, 10);
    		//ltoa(varin, varout);//(uint16_t)
    
                    /* Check variable length */
                    len = strlen(varout);
    
    		/* Loop through the array of integers */
    		//for(i = 0; i <= len + trim; i++) //
    		while(varout != '\0')
    		{
    			/* Blocking checks FIFO Transmitter Busy flag */
    			while(HWREG(UART2_BASE + UART_O_FR) &
    					UART_FR_BUSY) //UART_FR_TXFF|
    			{}
    			/* Transmit 2 bytes for the component Object named */
    			HWREG(UART2_BASE + UART_O_DR) = varout[i]; //8-bits 7:0
    
    			/* Increment pointer */
    			i++;
    
    			/* Match ends command string + '\0' NULL */
    			if(i >= len +1 + trim)
    			{
    				i = 0;
    				NextCmd = 2;
    				break;
    			}
    		}
    	}

  • The ltoa() '\0' NULL issue posted above is not obvious via F3 key results (libc.a). Until single stepping a break point via CCS debug does one even know ltoa() strips NULL or so it states. Subtracting 1 for NULL via length (sizeof) or strlen(s[]) does not correct the pointer race condition that occurs from the calling function passing incorrect value of an integer only variable to output via UARTDR register. I suspect HWREG macro is the culprit in integer output array pointer issues. Compiler complains UARTDR is uint32_t upon parsing any code attempting to pass int8_t or (char) from function header into UARTDR via either for or while loops.

    #include <string.h>
    
    #define BUFLEN 20
    
    _CODE_ACCESS int ltoa(long val, char *buffer)
    {
        char           tempc[BUFLEN];
        register char *bufptr;
        register int   neg = val < 0;
        register long  uval = val;
    
        *(bufptr = &tempc[BUFLEN - 1]) = 0;
    
        do {*--bufptr = abs(uval % 10) + '0';}  while(uval /= 10);
        if (neg) *--bufptr = '-';
    
        memcpy(buffer,bufptr, uval = (tempc + BUFLEN) - bufptr);
        return (uval - 1);    /* DON'T COUNT NULL TERMINATION */
    }

      

  • Seemingly CCS compilers have similar issue porting (int) as does VS warning, (yellow box). So (sizeof) the ported (int) via function header passing of (int) often truncates =<32 bits leaving voids in passed in data streams. Thus it requires odd pointer padding of strlen(i[s]) for each variable ported into UARTDB register via passed function (int dbyte). So far only 4 decimals, e.g. 1234 can be passed via (int) while loop porting (uint32_t variables), reference posted code snip above. 

  • What you highlighted in yellow is just a generic warning - you can probably find it in K&R, too - that is warning you that just because *your* compiler has an int of a certain size, not all compilers have an int of that size - it is implementation dependent. That is why they started adding the types with specified sizes.

  • Part of the integer conversion problem was for loop and strlen() did not play well with itoa() '\0' termination or ltoa() un-terminated.

    The placement of strlen() didn't make any difference to the passed in char pointer alignment of the for loop. It was only after changing code to a while loop the placement of strlen() made any difference and testing for itoa() string termination '\0'. The ltoa() imaginary termination char is being removed in (libc.a) and that is all we had to work with until later adding in itoa(). Some how the code removal of an imaginary termination character effects the integer pointer placement in the array.

     

  • George Mock said:
    The sizeof(uint32_t) is 4.  What do you expect instead?

    Hind sight you can not use sizeof with ltoa() to determine output string length since it adds '0' (0x30) to each integer of a variable. So the decimal number 1,2,3,4 of variable value requires the full 32 bits of type (int) and truncates decimal 5,6 in the break of case 1 code below.

    I modified the header of the call void and input char array to a (wint_t) 64 bit to help with this issue of passing uint32_t char strings via a while passing of integers to the case function. That additional step somehow aligns the array pointer in the case 1 input to output array conversion into ASCII.