My PPC device runs for a fairly long time on a watch battery and a pair of AAAs. Call me a hayseed, but I think this is really incredible. I wasn’t too surprised, then, when I found part of the reason for this long battery life is that my palmtop has a limited register set and it doesn’t have chip support for much floating-point math. Try as I might, I simply couldn’t rearrange program logic in a way that would get rid of unresolved externals that had to do with floating point number comparisons. I finally wrote workarounds that transform floating point numbers to integers and then operate them.
Figure 1: The FloatingPoint Example
Figure 1: FloatingPoint.cpp
// FloatingPoint.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "FloatingPoint.h" #include <commctrl.h> #include <windowsx.h> #include <math.h> #define MAX_LOADSTRING 100 #define BETWEEN 1 #define LESS_THAN 2 #define GREATER_THAN 3 // Global Variables: HINSTANCE hInst; // The current instance HWND hwndCB; // The command bar handle // Forward declarations of functions included in this code module: ATOM MyRegisterClass (HINSTANCE hInstance, LPTSTR szWindowClass); BOOL InitInstance (HINSTANCE, int); LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About (HWND, UINT, WPARAM, LPARAM); BOOL CALLBACK FloatingPtDlgProc (HWND, UINT, WPARAM, LPARAM); BOOL CheckFloatingPointRange( TCHAR* ); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; HACCEL hAccelTable; // Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_FLOATINGPOINT); // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } // // FUNCTION: MyRegisterClass() // // PURPOSE: Registers the window class. // // COMMENTS: // // This function and its usage is only necessary if you want // this code to be compatible with Win32 systems prior to the // 'RegisterClassEx' function that was added to Windows 95. // It is important to call this function so that the // application will get 'well formed' small icons associated // with it. // ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_FLOATINGPOINT)); wc.hCursor = 0; wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = szWindowClass; return RegisterClass(&wc); } // // FUNCTION: InitInstance(HANDLE, int) // // PURPOSE: Saves instance handle and creates main window // // COMMENTS: // // In this function, we save the instance handle in a global // variable and create and display the main program window. // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // The window class // name hInst = hInstance; // Store instance handle in our // global variable // Initialize global strings LoadString(hInstance, IDC_FLOATINGPOINT, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance, szWindowClass); LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); if (hwndCB) CommandBar_Show(hwndCB, TRUE); DialogBox( hInst, TEXT( "FLOATING_POINT" ), hWnd, FloatingPtDlgProc); return TRUE; } // // FUNCTION: WndProc(HWND, unsigned, WORD, LONG) // // PURPOSE: Processes messages for the main window. // // WM_COMMAND - process the application menu // WM_PAINT - Paint the main window // WM_DESTROY - post a quit message and return // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId) { case IDM_HELP_ABOUT: DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break; case IDM_FILE_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_CREATE: hwndCB = CommandBar_Create(hInst, hWnd, 1); CommandBar_InsertMenubar(hwndCB, hInst, IDM_MENU, 0); CommandBar_AddAdornments(hwndCB, 0, 0); break; case WM_DESTROY: CommandBar_Destroy(hwndCB); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // Mesage handler for the About box. LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { RECT rt, rt1; int DlgWidth, DlgHeight; // dialog width and height in pixel // units int NewPosX, NewPosY; switch (message) { case WM_INITDIALOG: // trying to center the About dialog if (GetWindowRect(hDlg, &rt1)) { GetClientRect(GetParent(hDlg), &rt); DlgWidth = rt1.right - rt1.left; DlgHeight = rt1.bottom - rt1.top ; NewPosX = (rt.right - rt.left - DlgWidth)/2; NewPosY = (rt.bottom - rt.top - DlgHeight)/2; // if the About box is larger than the physical // screen if (NewPosX < 0) NewPosX = 0; if (NewPosY < 0) NewPosY = 0; SetWindowPos(hDlg, 0, NewPosX, NewPosY, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } return TRUE; case WM_COMMAND: if ((LOWORD(wParam) == IDOK) || (LOWORD(wParam) == IDCANCEL)) { EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; } return FALSE; } BOOL CALLBACK FloatingPtDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { HWND hWndEdit; TCHAR tszNumberBuff[8]; switch (message) { case WM_INITDIALOG: hWndEdit = GetDlgItem( hDlg, IDC_FLOAT_NUMBER ); //limit input to 6 characters Edit_LimitText( hWndEdit, 6 ); break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_TEST: memset( tszNumberBuff, 0x0, sizeof(tszNumberBuff)); GetDlgItemText( hDlg, IDC_FLOAT_NUMBER, &tszNumberBuff[0], 6 ); if( !CheckFloatingPointRange( &tszNumberBuff[0])) { MessageBox(hDlg, TEXT(" Input not in range "), TEXT( "Floating Point Test " ), MB_OK ); } else { MessageBox(hDlg, TEXT(" Input within range "), TEXT( "Floating Point Test " ), MB_OK ); } break; case IDCANCEL: EndDialog(hDlg, LOWORD(wParam)); return TRUE; default: break; } break; } return FALSE; } //////////////////////////////////////////////////////// // Some PPCs don't do floating pt compares, // so here is a work around //////////////////////////////////////////////////////// BOOL CheckFloatingPointRange( TCHAR* pszFloatString ) { int iValid, iInputDecimal, iInputSign, iCompareInput; int iThreshDecimal, iThreshSign ; int iThreshStrlen, iInputStrlen; int iInput, iThreshold, iExp, iDivisor; char *pszBuffer, *psz1stInputZero, *psz1stThreshZero; char szInputBuff[9]; double dInput; double dThreshold = 0; double* pThreshold; TCHAR *stopstring; //get validation type iValid = BETWEEN; //get user input dInput = wcstod(pszFloatString, &stopstring); //set the upper threshold value dThreshold = 8.95; //get input digits, magnitude & sign pszBuffer = _ecvt( dInput, 8, &iInputDecimal, &iInputSign ); //copy the string before the buffer contents are destroyed strcpy( szInputBuff, pszBuffer ); //find the location of the first 0, calculate number digits psz1stInputZero = strchr( szInputBuff, '0'); iInputStrlen = psz1stInputZero - szInputBuff; //convert to integer iInput = atoi( szInputBuff ); //apply correct sign if( iInputSign ) { iInput *= -1; } //get threshold digits, magnitude & sign pszBuffer = _ecvt( dThreshold, 8, &iThreshDecimal, &iThreshSign ); //find the location of the first 0, calculate number digits psz1stThreshZero = strchr( pszBuffer, '0'); iThreshStrlen = psz1stThreshZero - pszBuffer; //convert to integer iThreshold = atoi( pszBuffer ); //apply correct sign if( iThreshSign ) { iThreshold *= -1; } switch( iValid ) { case BETWEEN: case LESS_THAN: //if the input sign is greater, fail and bail //if the sign == 0, the number we converted is positive if( iInputSign == 0 && iThreshSign != 0 ) {return FALSE; } //if the input magnitude is greater, fail & bail //The decimal parameter points to an integer value //giving the position of the decimal point with respect //to the beginning of the string. if( iInputDecimal > iThreshDecimal ) {return FALSE; } if( iInputDecimal < iThreshDecimal ) {return TRUE; } // if we get to here, we have to compare // the digits to find the larger number //divide away the padding to correct the magnitudes -- //use all the decimal digits in the longest string iExp = ( iInputStrlen <= iThreshStrlen )? 8 - iThreshStrlen: 8 - iInputStrlen; iDivisor = (int)pow( 10, (double)iExp); iCompareInput = iInput / iDivisor; iThreshold = iThreshold / iDivisor; if( iThreshold < iCompareInput ) {return FALSE;} //return for Greater than if(iValid == LESS_THAN) {return TRUE;} //if this is a range, get 2nd bound if(iValid == BETWEEN) { //set lower range bound dThreshold= 4.32; //get threshold digits, magnitude & sign pszBuffer = _ecvt( dThreshold, 8, &iThreshDecimal, &iThreshSign ); //find the location of the first 0, calculate //number digits psz1stThreshZero = strchr( pszBuffer, '0'); iThreshStrlen = psz1stThreshZero - pszBuffer; //convert to integer iThreshold = atoi( pszBuffer ); //apply correct sign if( iThreshSign ) { iThreshold *= -1; } } //FALL THRU!! case GREATER_THAN: //test uses the first threshold val if GT, second if //BTWN //if the input sign is greater, fail and bail //if the sign 1= 0, the number we converted is negative if( iInputSign != 0 && iThreshSign == 0 ) {return FALSE; } //if the input magnitude is greater, fail & bail //The decimal parameter points to an integer value //giving the position of the decimal point with respect //to the beginning of the string. if( iInputDecimal < iThreshDecimal ) {return FALSE; } if( iInputDecimal > iThreshDecimal ) {return TRUE; } // if we get to here, we have to compare // the digits to find the larger number //divide away the padding to correct the magnitudes-- //use all the decimal digits in the longest string iExp = ( iInputStrlen <= iThreshStrlen )? 8 - iThreshStrlen: 8 - iInputStrlen; iInput = iInput / (int)pow( 10, (double)iExp); iThreshold = iThreshold / (int)pow( 10, (double)iExp ); if( iThreshold > iInput ) {return FALSE;} else {return TRUE;} default: //all other cases are self validating return TRUE; break; } //end switch }
The math transforms are done in the CheckFloatingPointRange() function. The tricky thing about converting floating point numbers to integers is getting the place values translated correctly, so if you write a floating point routine, pay careful attention to the length of the strings that you end up with as you translate between characters and integers.
//get input digits, magnitude & sign pszBuffer = _ecvt( dInput, 8, &iInputDecimal, &iInputSign ); //copy the string before the buffer contents are destroyed strcpy( szInputBuff, pszBuffer );