完成我的项目后(使用带键盘4x4,LCD和4个LED的PIC18计算器),我在乘法运算中遇到了奇怪的问题
当乘以奇数(> 1)*偶数(> 0)时,结果不完全正确,例如,3 * 4 = 11.99999
====================
我将尝试描述这种情况:
用户按下键盘上的按钮,然后程序从键盘中取出char并将其存储到char [](一次为char),最后我们有两个char []操作数[13]和操作数[ 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);
}
}
}
}
答案 0 :(得分:4)
你正在使用浮点类型,它们并不总能保证在某些数学运算下完全准确地表示。
可能值得进行某种检查以查看其中一个或两个操作数是否为整数,然后如果可以,则使用int
类型。
答案 1 :(得分:3)
原因是它存储为二进制浮点,而不是内部的十进制浮点数,这意味着一些容易用十进制表示的数字不能用二进制表示完全精确,因此四舍五入到最接近的可能值。 / p>
答案 2 :(得分:1)
通常,两个小整数浮点数的直接乘法会产生x.0
结果。但是,对于更复杂的浮点数学,你必须采取任何结果都是估计的态度,并且会有一小部分错误。通常它取决于输入中的错误量,但至少您应该将任何仅在最后一个可表示数字中关闭的结果视为“正确”。一般来说,期望浮点数学的确切答案是错误的。
另请注意,在这种情况下,它似乎试图告诉您答案是11.9(9重复)。在数学上与12相同。所以我不会真的认为这个答案会出现“错误”。