0

After finishing my project (The Calculator using PIC18 with Keypad 4x4, LCD and 4 LEDs), I faced wierd issue in the multiplication operation

When multiply odd (> 1) * even (> 0) the result is not exactly correct, for example, 3 * 4 = 11.99999

====================

Here I would try to descripe the situation:

The user press a button of the keypad, then the program takes the char from the keypad and stores it into char[] (A char at a time) so finally we have two char[] Operand[13] and Operand[13]. After Entering Operand1 and Operand2 the program converts it to floating number using atof() function then apply some operation ('+', '-', '*' or '/') and stores the result in int Result. Finally, the program convert this int to char[] using FloatToStr() function and display it on the LCD.

=====================

Here is my code: (Sorry, it's kinda long but I want you help me solving this issue)

unsigned char Key[4][4] = {{'1','2','3','4'},                                   // Keypad 4x4 Matrix
                           {'5','6','7','8'},
                           {'9','0','.','E'},
                           {'+','-','*','/'}};

unsigned char Operand_String[2][13], Operation_String, Result_String[15];       // Where we store the operand, operation & Result as chars
unsigned char Row, Column;                                                      // Row & Column determine the location of Key in the Keypad matrix that the user pressed
unsigned char State;                                                            // 1: Operand1 - 2: Operand2 - 3: Operation - 4: Result
unsigned int EntryCount;                                                        // Counter for counts entries on LCD
unsigned int Operand1_length, Operand2_length;                                  // Lengths of Operand1 & Operand2
bit Direction;                                                                  // 1: right - 0: left
bit Operand1_Sign, Operand2_Sign;                                               // 1: negative - 0: positive
signed float Operand1, Operand2, Result;                                        // Fields where we store Operand1, Operand2 and Result

typedef unsigned int boolean;                                                   // ** DEFINING **
#define false 0                                                                 // **  BOOLEAN **
#define true (!false)                                                           // **   TYPE   **

boolean ButtonIsPressed = false;                                                // Flag that indicates whether a button is pressed or no button is pressed
boolean FloatingPointIncluded = false;                                          // Flag that indicates if there is a floating point in the LCD or not
boolean Operand1_signed = false;                                                // Flag that indicates if Operand1 has a sign or not
boolean Operand2_signed = false;                                                // Flag that indicates if Operand2 has a sign or not


// ***** Lcd pinout settings *****
sbit LCD_RS at RD4_bit;
sbit LCD_EN at RD5_bit;
sbit LCD_D7 at RD3_bit;
sbit LCD_D6 at RD2_bit;
sbit LCD_D5 at RD1_bit;
sbit LCD_D4 at RD0_bit;

// ***** Pin Direction *****
sbit LCD_RS_Direction at TRISD4_bit;
sbit LCD_EN_Direction at TRISD5_bit;
sbit LCD_D7_Direction at TRISD3_bit;
sbit LCD_D6_Direction at TRISD2_bit;
sbit LCD_D5_Direction at TRISD1_bit;
sbit LCD_D4_Direction at TRISD0_bit;

// ***** End LCD module connections *****




// ***** Method that determines no. of Row and Column of keypad matrix where the user presses the button of that location on the keypad *****
void scan_key()
{
    unsigned char portValue[4][4] = {{0b11101110, 0b11101101, 0b11101011, 0b11100111},
                                     {0b11011110, 0b11011101, 0b11011011, 0b11010111},
                                     {0b10111110, 0b10111101, 0b10111011, 0b10110111},
                                     {0b01111110, 0b01111101, 0b01111011, 0b01110111}};

    unsigned char temp[4] = {0B11111110, 0B11111101, 0B11111011, 0B11110111};

    unsigned int loop_col = 1;
    unsigned int loop_row = 1;

    for (loop_col = 1; loop_col < 5; loop_col++)
    {
         PORTB = temp[loop_col - 1];
         for (loop_row = 1; loop_row < 5; loop_row++)
         {
              if ( PORTB == portValue[loop_row - 1][loop_col - 1])
              {
                   Row = loop_row;
                   Column = loop_col;
                   return;
                   }
         }

         PORTB = 0B11110000;
    }

}


// ***** Interrupt Service Routine (ISR) *****

void interrupt() org 0x08
{
     if (INTCON.TMR0IF)           // Timer0 Interrupt
     {
           scan_key();
           //Delay_ms(40);
           INTCON.RBIE = 1;
           INTCON.TMR0IF = 0;
     }
     else if (INTCON.RBIF)        // PORTB Interrupt
     {
           INTCON.TMR0IF = 1;
           INTCON.TMR0IE = 1;
           INTCON.RBIE = 0;
           INTCON.RBIF = 0;
           ButtonIsPressed = true;
     }
}


// ***** Method that calculates the result of the arithmatic Operation *****

float CalculateResult()
{
     Operand1 = atof(Operand_String[0]);
     Operand2 = atof(Operand_String[1]);
     if(Operand1_sign == 1) Operand1 = - Operand1;
     if(Operand2_sign == 1) Operand2 = - Operand2;
     switch(Operation_String)
     {
          case '+': Result = Operand1 + Operand2; break;
          case '-': Result = Operand1 - Operand2; break;
          case '*': Result = Operand1 * Operand2; break;
          case '/': Result = Operand1 / Operand2; break;
     }
     return Result;
}


// ***** Method that makes LEDs blink *****

void LEDsBlink(int iteration)
{
     char PORTA_Temp;
     int i;
     PORTA_Temp = PORTA;
     if(iteration < 0)
     {
           PORTA = ~PORTA;
           Delay_ms(200);
     }
     else
     {
          for(i = 0; i < iteration; i++)
          {
                PORTA = 0x0F;
                Delay_ms(50);
                PORTA = 0x00;
                Delay_ms(50);
          }
          PORTA = PORTA_Temp;
     }
}


// ***** Method that resets the variables *****

void Reset_Values()
{
     EntryCount = 0;
     State = 1;
     Row = 0;
     Column = 0;
     Direction = 0;
     Operand1_Sign = 0;
     Operand2_Sign = 0;
     Operand1 = 0;
     Operand2 = 0;
     Result = 0;
     memset(Operand_String, 0, 2 * 13);
     memset(Result_String, 0, 15);
     ButtonIsPressed = false;
     Operand1_signed = false;
     Operand2_signed = false;
     PORTA = 0x0F;             // Turn on the 4 LEDs
}

void main()
{
     // ***** Initializations of PIC18F4550 *****

     TRISA = 0;                // Configure the 4 LEDs as output
     PORTA = 0x0F;             // Turn on the 4 LEDs
     TRISB = 0xF0;             // Configure RB0 ~ RB3 as outputs & RB4 ~ RB7 as inputs
     PORTB = 0xF0;             // Assign 0xF0
     OSCCON = 0x63;            // 4 MHz - Internal oscillator
     INTCON2.B7 = 0;           // PORTB pull-ups are enabled by individual port latch values
     INTCON.RBIF = 0;          // Reset the interrupt flag
     INTCON.RBIE = 1;          // RB Change interrupt ON
     INTCON.GIE = 1;           // Global interrupts ON
     ADCON1 = 0b00001111;      // Digital inputs

     // ***** Initializations of LCD *****

     Lcd_Init();                                // Initialize LCD
     Lcd_Cmd(_LCD_CLEAR);                       // Clear LCD
     Lcd_Cmd(_LCD_CURSOR_OFF);                  // Cursor off
     Lcd_Out(1, 2, "Fouad Al-Malki");           // ** WELCOME **
     Lcd_Out(2, 4, "CALCULATOR");               // ** MESSAGE **
     Delay_ms(2000);                            // delay for 2 seconds
     Lcd_Cmd(_LCD_CLEAR);                       // Clear DCD
     Lcd_Out(1, 4, "Operand1: ");               // Write "Operand1: " at first row
     Lcd_Cmd(_LCD_SECOND_ROW);                  // Make current position at second row
     Lcd_Cmd(_LCD_BLINK_CURSOR_ON);             // Cursor on

     // ***** Reset all values *****

     Reset_Values();

     while(1)
     {

          // ***** Control of LCD *****

          if(ButtonIsPressed)
          {
                if(State == 1)
                {
                     if(Key[row-1][column-1] != 'E')
                     {
                          if((EntryCount <= 13 && Key[row-1][column-1] != '+' && Key[row-1][column-1] != '-' && Key[row-1][column-1] != '/' && Key[row-1][column-1] != '*'))
                          {
                               if((Key[row-1][column-1] == '.' && !FloatingPointIncluded && EntryCount > 0) || Key[row-1][column-1] != '.')
                               {
                                    Lcd_Chr_Cp(Key[row-1][column-1]);
                                    Operand_String[0][EntryCount] = Key[row-1][column-1];
                                    EntryCount++;
                                    if(Key[row-1][column-1] == '.') FloatingPointIncluded = true;
                               }
                               else LEDsBlink(3);
                          }
                          else if(!Operand1_signed && EntryCount == 0 && (Key[row-1][column-1] == '+' || Key[row-1][column-1] == '-'))
                          {
                               if(Key[row-1][column-1] == '+') Operand1_Sign = 0;
                               else if(Key[row-1][column-1] == '-') Operand1_Sign = 1;
                               Lcd_Chr_Cp(Key[row-1][column-1]);
                               Operand1_signed = true;
                          }
                          else LEDsBlink(3);
                     }
                     else if(Key[row-1][column-1] == 'E' && EntryCount != 0)
                     {
                          State = 2;
                          FloatingPointIncluded = false;
                          Operand1_length = EntryCount;
                          EntryCount = 0;
                          Row = 0;
                          Column = 0;
                          Lcd_Cmd(_LCD_CLEAR);       // Clear display
                          Lcd_Out(1, 4, "Operand2: ");
                          Lcd_Cmd(_LCD_SECOND_ROW);
                          Lcd_Cmd(_LCD_BLINK_CURSOR_ON);  // Cursor on
                          if(PORTA.B0 == 1)
                          {
                               PORTA = 0x08;
                          }
                     }
                     else LEDsBlink(3);
                }
                else if(State == 2)
                {
                     if(Key[row-1][column-1] != 'E')
                     {
                          if((EntryCount <= 13 && Key[row-1][column-1] != '+' && Key[row-1][column-1] != '-' && Key[row-1][column-1] != '/' && Key[row-1][column-1] != '*'))
                          {
                               if((Key[row-1][column-1] == '.' && !FloatingPointIncluded && EntryCount > 0) || Key[row-1][column-1] != '.')
                               {
                                    Lcd_Chr_Cp(Key[row-1][column-1]);
                                    Operand_String[1][EntryCount] = Key[row-1][column-1];
                                    EntryCount++;
                                    if(Key[row-1][column-1] == '.') FloatingPointIncluded = true;
                               }
                               else LEDsBlink(3);
                          }
                          else if(!Operand2_signed && EntryCount == 0 && (Key[row-1][column-1] == '+' || Key[row-1][column-1] == '-'))
                          {
                               if(Key[row-1][column-1] == '+') Operand2_Sign = 0;
                               else if(Key[row-1][column-1] == '-') Operand2_Sign = 1;
                               Lcd_Chr_Cp(Key[row-1][column-1]);
                               Operand2_signed = true;
                          }
                          else LEDsBlink(3);
                     }
                     else if(Key[row-1][column-1] == 'E' && EntryCount != 0)
                     {
                          State = 3;
                          FloatingPointIncluded = false;
                          Operand2_length = EntryCount;
                          EntryCount = 0;
                          Row = 0;
                          Column = 0;
                          Lcd_Cmd(_LCD_CLEAR);       // Clear display
                          Lcd_Out(1, 4, "Operation: ");
                          Lcd_Cmd(_LCD_SECOND_ROW);
                          Lcd_Cmd(_LCD_BLINK_CURSOR_ON);  // Cursor on
                          if(PORTA.B0 == 1)
                          {
                               PORTA = PORTA << 1;
                               Direction = 0;
                          }
                     }
                     else LEDsBlink(3);
                }
                else if(State == 3)
                {
                     if(Key[row-1][column-1] != 'E')
                     {
                          if(EntryCount == 0 && (Key[row-1][column-1] == '+' || Key[row-1][column-1] == '-' || Key[row-1][column-1] == '/' || Key[row-1][column-1] == '*'))
                          {
                               Lcd_Chr_Cp(Key[row-1][column-1]);
                               Operation_String = Key[row-1][column-1];
                               EntryCount++;
                          }
                          else LEDsBlink(3);
                     }
                     else if(Key[row-1][column-1] == 'E' && EntryCount != 0)
                     {
                          State = 4;
                          FloatingPointIncluded = false;
                          EntryCount = 0;
                          Row = 0;
                          Column = 0;
                          Lcd_Cmd(_LCD_CLEAR);       // Clear display
                          Lcd_Out(1, 3, "The Result: ");
                          Lcd_Cmd(_LCD_SECOND_ROW);
                          Lcd_Cmd(_LCD_CURSOR_OFF);  // Cursor off
                          Result = CalculateResult();
                          //printOut(Result, "/*rn");
                          FloatToStr(Result, Result_String);
                          Lcd_Out(2,1,Result_String);
                          PORTA = 0x0F;
                     }
                     else LEDsBlink(3);
                }
                else if(State == 4)
                {
                     if(Key[row-1][column-1] == 'E')
                     {
                          Reset_Values();
                          Lcd_Cmd(_LCD_CLEAR);       // Clear display
                          Lcd_Out(1, 4, "Operand1: ");
                          Lcd_Cmd(_LCD_SECOND_ROW);
                          Lcd_Cmd(_LCD_BLINK_CURSOR_ON);  // Cursor on
                          PORTA == 0x0F;
                     }
                }
                ButtonIsPressed = false;

          }



          // ***** Control of LEDs *****

          else
          {
               if(State == 1)
               {
                     if(PORTA == 0x0F) PORTA = 0x01;
                     Delay_ms(300);
                     if(PORTA < 0x08) PORTA = PORTA << 1;
                     if(PORTA == 0x08)
                     {
                          Delay_ms(300);
                          PORTA = 0x01;
                     }

               }
               if(State == 2)
               {
                     Delay_ms(300);
                     PORTA = PORTA >> 1;
                     if(STATUS.C == 1)
                     {
                          PORTA = 0x08;
                          STATUS.C = 0;
                     }
               }
               if(State == 3)
               {
                    Delay_ms(300);
                    if (Direction == 0) {
                         PORTA = PORTA >> 1;
                         if (PORTA.B0 == 1) Direction = 1;
                    }
                    else
                    {
                         PORTA = PORTA << 1;
                         if (PORTA.B3 == 1) Direction = 0;
                    }

               }
               if(State == 4)
               {
                    LEDsBlink(-1);
               }
          }
     }

}
Eng.Fouad
  • 107,075
  • 62
  • 298
  • 390
  • 7
    Please reduce the code sample to a [minimal test case](http://sscce.org/) so we don't have to wade through extraneous code. – outis May 04 '11 at 18:45
  • Where in your code do you see that as a result? floating point multiplication should work fine for the example you quote above. You should whittle down your code to the smallest possible that reproduces what you see. – Jim Buck May 04 '11 at 18:46
  • 2
    This looks *almost* like C, but it isn't; C doesn't have bits, sbits, "at X" declaration modifiers, binary numeric literals, or global variables that you can use without declaring them at all. – zwol May 04 '11 at 18:46
  • 1
    `signed float`! LOL I had no idea it could be used. What happens if you change all `floats` to `doubles`? **EDIT: `signed float` is not recognized by the Standard** – pmg May 04 '11 at 18:49
  • 2
    Yeah. If it is really happening as you say ( >1 odd number * >0 even number = number with .9-repeating), then you should be able to find a one-line example that exhibits it. – T.E.D. May 04 '11 at 18:49
  • This is probably just the usual problem with binary floating-point inexactness, see http://stackoverflow.com/questions/1839422/strange-output-in-comparision-of-float-with-float-literal , http://stackoverflow.com/questions/1839225/float-addition-promoted-to-double , etc. – zwol May 04 '11 at 18:51
  • This is mikroC, isn't it like C ? – Eng.Fouad May 04 '11 at 18:57
  • Very bad question. Don't paste your whole program to ask about a problem that could be reproduced in a few lines.... – R.. GitHub STOP HELPING ICE May 04 '11 at 19:38

3 Answers3

4

You're using float types, which aren't always guaranteed to be represented with complete accuracy under some mathematical operations.

It might be worth doing some sort of check to see if either or both of the operands are integers, and then use an int type if you can.

Platinum Azure
  • 41,456
  • 9
  • 100
  • 130
3

The reason is it is stored as binary floating-point, not decimal floating-point internally, meaning some numbers easily represented in decimal are impossible to represent to full accuracy in binary, and so are rounded to the closest possible value.

Andrea
  • 17,448
  • 4
  • 35
  • 62
  • 4
    Integers with absolute value ≤ 2**FLT_MANT_DIG are exactly representable as floats. While issues with floating point are involved here, the problem likely lies somewhere other than the representations of 3.0, 4.0 and 12.0. – outis May 04 '11 at 19:26
1

Normally a straight multiplication of two small integer floats like that would produce a x.0 result. However, for more complicated floating-point math you really have to take the attitude that any results are estimates and there will be a certian small amount of error. Often it depends on the amount of error in the inputs, but at the least you should consider any result that is only off in the last representable digit as "correct". In general, expecting exact answers out of floating-point math is wrong.

Also note that in this case it appears to be attempting to tell you that the answer is 11.9(9 repeating). Mathematically that is the same as 12. So I wouldn't really consider this answer it is coming up with "wrong".

T.E.D.
  • 41,324
  • 8
  • 64
  • 131
  • Floating point math can give exact results if you want it to and if you know how to use it. And your second paragraph just makes no sense. The 9 is not "repeating" or it would get rounded to 12 when printed rather than cutting off at a 9. – R.. GitHub STOP HELPING ICE May 04 '11 at 19:43