0

私のプロジェクト (キーパッド 4x4、LCD、および 4 つの LED を備えた PIC18 を使用した電卓) を終了した後、乗算操作で奇妙な問題に直面しました。

奇数 (> 1) * 偶数 (> 0) を乗算すると、結果は正確には正しくありません。たとえば、3 * 4 = 11.99999 です。

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

ここで、状況を説明しようとします。

ユーザーがキーパッドのボタンを押すと、プログラムはキーパッドから char を取得し、それを char[] (一度に 1 つの文字) に格納するため、最終的に 2 つの char[] Operand[13] と Operand[13] が作成されます。Operand1 と Operand2 を入力した後、プログラムは atof() 関数を使用して浮動小数点数に変換し、演算 ('+'、'-'、'*' または '/') を適用し、結果を int Result に格納します。最後に、プログラムは FloatToStr() 関数を使用してこの int を char[] に変換し、LCD に表示します。

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

これが私のコードです:(すみません、ちょっと長いですが、この問題を解決するのを手伝ってください)

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

}
4

3 に答える 3

4

フロートタイプを使用していますが、一部の数学演算では完全な精度で表現されることが常に保証されているわけではありません。

ある種のチェックを行って、オペランドのいずれかまたは両方が整数であるかどうかを確認し、int可能であれば型を使用することをお勧めします。

于 2011-05-04T18:47:02.747 に答える
3

その理由は、内部では10進浮動小数点ではなく、2進浮動小数点として格納されるためです。つまり、10進数で簡単に表現できる数値の中には、2進数で完全に正確に表現できないものがあるため、可能な限り近い値に丸められます。

于 2011-05-04T18:48:25.777 に答える
1

通常、そのような2つの小さな整数のfloatをまっすぐに乗算すると、x.0結果が生成されます。ただし、より複雑な浮動小数点演算の場合、結果は推定値であり、証明書に少量のエラーが発生するという態度をとる必要があります。多くの場合、入力のエラーの量によって異なりますが、少なくとも、最後の表現可能な桁でのみオフになっている結果は「正しい」と見なす必要があります。一般に、浮動小数点演算から正確な答えを期待するのは間違っています。

また、この場合、答えは11.9(9回繰り返される)であることを伝えようとしているように見えることにも注意してください。数学的には12と同じです。ですから、この答えが「間違っている」と思いついているとは思いません。

于 2011-05-04T18:58:47.867 に答える