|
|
View previous topic :: View next topic |
Author |
Message |
newguy
Joined: 24 Jun 2004 Posts: 1909
|
math error - signed int16 and negative numbers |
Posted: Tue Feb 06, 2007 5:25 pm |
|
|
I spent most of the day trying to figure out why a program wouldn't give me correct results, and it all boiled down to one line giving incorrect results when a variable became negative. If a signed int16 variable is positive and is multiplied by 1440, the results are correct. If the variable becomes negative, the results are wrong.
Here is some output from the program below:
Code: | 1. A = 3
2. B = 4320 // correct
1. A = 2
2. B = 2880 // correct
1. A = 1
2. B = 1440 // correct
1. A = 0
2. B = 0 // correct
1. A = -1
2. B = -1184 // what the ?
1. A = -2
2. B = -2624 // huh?
1. A = -3
2. B = -4064 // dammit! |
PCWH version 3.236
Code: | #include <18F4580.h>
#device adc=8
#use delay(clock=20000000,RESTART_WDT)
#fuses HS, BROWNOUT, BORV46, PUT, STVREN, NOLVP
#use rs232(baud=115200,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
#define TX_BUFFER_SIZE 80
int8 tx_rd_index = 0, tx_wr_index = 0, tx_counter = 0;
unsigned int8 tx_buffer[TX_BUFFER_SIZE + 1];
signed int16 a, b;
#int_TBE
void TBE_isr(void) {
if (tx_counter != 0) {
putc(tx_buffer[tx_rd_index]);
if (++tx_rd_index > TX_BUFFER_SIZE) {
tx_rd_index = 0;
}
tx_counter--;
if (tx_counter == 0) {
disable_interrupts(INT_TBE);
}
}
}
void bputc(int c) {
int restart = 0;
while (tx_counter > (TX_BUFFER_SIZE - 1)) {
restart_wdt();
}
if (tx_counter == 0) {
restart = 1;
}
tx_buffer[tx_wr_index++] = c;
if (tx_wr_index > TX_BUFFER_SIZE) {
tx_wr_index = 0;
}
tx_counter++;
if (restart == 1) {
enable_interrupts(INT_TBE);
}
}
void main() {
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
setup_wdt(WDT_ON);
setup_timer_0(RTCC_INTERNAL|RTCC_OFF|RTCC_8_bit);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
enable_interrupts(global);
a = 3;
while (TRUE) {
restart_wdt();
printf(bputc,"1. A = %ld\n\r",a);
b = a * 1440;
printf(bputc,"2. B = %ld\n\r",b);
if (--a == -4) {
a = 3;
}
delay_ms(1000);
}
} |
Note that I get the same behaviour if I put a "L" behind the 1440:
Here's the list file, if it helps. I don't think this is a printf problem, but rather is some sort of math bug. I'd appreciate some input.
Code: | .................... while (TRUE) {
.................... restart_wdt();
0304: CLRWDT
.................... printf(bputc,"1. A = %ld\n\r",a);
0306: CLRF x70
0308: MOVF x70,W
030A: RCALL 00A0
030C: INCF x70,F
030E: MOVWF 00
0310: MOVWF x7A
0312: RCALL 0126
0314: MOVLW 07
0316: SUBWF x70,W
0318: BNZ 0308
031A: MOVLW 10
031C: MOVWF FE9
031E: MOVFF 6D,72
0322: MOVFF 6C,71
0326: RCALL 0160
0328: MOVLW 0A
032A: MOVWF x7A
032C: RCALL 0126
032E: MOVLW 0D
0330: MOVWF x7A
0332: RCALL 0126
.................... b = a * 1440;
0334: MOVFF 6D,71
0338: MOVFF 6C,70
033C: MOVLW 05
033E: MOVWF x73
0340: MOVLW A0
0342: MOVWF x72
0344: BRA 024A
0346: MOVFF 02,6F
034A: MOVFF 01,6E
.................... printf(bputc,"2. B = %ld\n\r",b);
034E: CLRF x70
0350: MOVF x70,W
0352: RCALL 00C8
0354: INCF x70,F
0356: MOVWF 00
0358: MOVWF x7A
035A: RCALL 0126
035C: MOVLW 07
035E: SUBWF x70,W
0360: BNZ 0350
0362: MOVLW 10
0364: MOVWF FE9
0366: MOVFF 6F,72
036A: MOVFF 6E,71
036E: RCALL 0160
0370: MOVLW 0A
0372: MOVWF x7A
0374: RCALL 0126
0376: MOVLW 0D
0378: MOVWF x7A
037A: RCALL 0126
.................... if (--a == -4) {
037C: MOVF x6C,W
037E: BTFSC FD8.2
0380: DECF x6D,F
0382: DECF x6C,F
0384: MOVF x6C,W
0386: SUBLW FC
0388: BNZ 0394
038A: INCFSZ x6D,W
038C: BRA 0394
.................... a = 3;
038E: CLRF x6D
0390: MOVLW 03
0392: MOVWF x6C
.................... }
.................... delay_ms(1000);
0394: MOVLW 04
0396: MOVWF x70
0398: MOVLW FA
039A: MOVWF x71
039C: BRA 026A
039E: DECFSZ x70,F
03A0: BRA 0398
.................... }
03A2: BRA 0304 |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Feb 06, 2007 6:32 pm |
|
|
I made a shorter test program and found that the order of
operations is important. I tested this with PCH vs. 3.249.
Here are the results from the program below.
Quote: |
B = -1184 // It fails if the variable comes first.
B = -1440 // It's correct if the constant comes first.
|
It doesn't matter if I put 'L' after all the constants. It doesn't help.
Code: |
#include <18F4580.h>
#fuses HS, NOWDT, BROWNOUT, PUT, NOLVP
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
signed int16 a, b;
//=======================
void main()
{
// This one fails.
a = -1;
b = a * 1440;
printf("B = %ld \n\r", b);
// Now put the 1440 first. It works.
a = -1;
b = 1440 * a;
printf("B = %ld \n\r", b);
while(1);
} |
This item from the old versions page is suspicious:
Quote: | 3.170 A type conversion problem involving signed numbers and constants is fixed. |
Maybe the old regression testing problem ?
I'm not sure that they fixed it then, because I installed vs. 3.173 and
it failed. I don't have 3.170. It also fails with vs. 4.023.
Then I went back to 3.148 and it worked:
Quote: |
B = -1440
B = -1440
|
|
|
|
newguy
Joined: 24 Jun 2004 Posts: 1909
|
|
Posted: Tue Feb 06, 2007 6:48 pm |
|
|
PCM,
Thanks for the tip. I'll switch the order in my real program. How frustrating to chase this thing all day!
I'd alert them to this bug but the last time that I did tell them about a bug was about 18 months ago and they never did fix it in the end. Probably no use in alerting them to this. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|