Почему нечетное * четное не возвращает точное значение в C? - PullRequest
0 голосов
/ 04 мая 2011

После завершения моего проекта (Калькулятор, использующий PIC18 с клавиатурой 4x4, ЖК-дисплеем и 4 светодиодами), я столкнулся со странной проблемой в операции умножения

При умножении нечетного (> 1) * четного (> 0)результат не совсем корректный, например, 3 * 4 = 11.99999

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

Здесь я бы попробовалчтобы описать ситуацию:

Пользователь нажимает кнопку на клавиатуре, затем программа берет символ с клавиатуры и сохраняет его в символе [] (символ за раз), так что, наконец, у нас есть два символа [Операнд [13] и Операнд [13].После ввода операндов 1 и операндов 2 программа преобразует их в плавающее число с помощью функции atof (), затем применяет некоторую операцию ('+', '-', '*' или '/') и сохраняет результат в int Result.Наконец, программа преобразует этот int в char [] с помощью функции FloatToStr () и отображает его на ЖК-дисплее.

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

Вот мой код: (Извините, это довольно долго, но я хочу, чтобы вы помогли мне решить эту проблему)

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);
               }
          }
     }

}

Ответы [ 3 ]

4 голосов
/ 04 мая 2011

Вы используете типы с плавающей точкой, которые не всегда гарантированно представлены с полной точностью при некоторых математических операциях.

Возможно, стоит провести какую-то проверку, чтобы увидеть, являются ли один или оба операнда целыми числами, а затем использовать тип int, если можете.

3 голосов
/ 04 мая 2011

Причина в том, что он хранится как двоичная с плавающей запятой, а не как внутренняя десятичная с плавающей запятой, что означает, что некоторые числа, легко представляемые в десятичном виде, невозможно представить с полной точностью в двоичном формате, и поэтому они округляются до ближайшего возможного значения.

1 голос
/ 04 мая 2011

Обычно прямое умножение двух маленьких целочисленных чисел с плавающей точкой дает результат x.0. Тем не менее, для более сложной математики с плавающей запятой вы действительно должны принять позицию, что любые результаты оценки , и будет определенная небольшая ошибка. Часто это зависит от количества ошибок на входах, но, по крайней мере, вы должны рассматривать любой результат, который отключен только в последней представимой цифре, как «правильный». В общем, ожидание точных ответов по математике с плавающей точкой неверно .

Также обратите внимание, что в этом случае он пытается сказать вам, что ответ - 11,9 (9 повторений). Математически это то же самое, что и 12. Поэтому я бы не стал считать, что этот ответ придет с «неправильным».

...