PRB: RFX Unnecessarily Updates Floating Point FieldsLast reviewed: August 7, 1997Article ID: Q163244 |
1.51 1.52 | 2.00 2.10 2.20 4.00 4.10 4.20 5.00
WINDOWS | WINDOWS NTkbtshoot kbusage kbcode The information in this article applies to:
SYMPTOMSWhen you use the Microsoft Foundation Classes (MFC) ODBC database classes to access tables containing floating point numeric data, you may observe that some numeric fields are updated unnecessarily. Fields that contain values that were not modified may be marked as dirty and, as a result, are unnecessarily updated.
CAUSERFX_Double and RFX_Single are the RFX functions that the wizards select to handle the SQL data types SQL_DOUBLE and SQL_REAL, respectively. Both of these functions detect when recordset member variable values have changed and require an update to change the corresponding fields in the actual database table. The detection of dirty fields is based on caching the original value of the recordset member variable and comparing it to the current value of the variable. The comparison that Microsoft Foundation Classes uses to detect dirty floating point values has always been some form of exact comparison. It is generally not recommended that you perform exact comparisons of floating point values because when store a decimal fraction in binary it is inherently imprecise. Rounding and truncation can occur when these values are processed (particularly when the Floating Point Unit (FPU) is invoked) and can lead two values that were initially exactly equal to eventually be only nearly equal. It is preferable to either treat a floating point field as character data that is immune to rounding errors or incorporate a small offset value into comparisons (known as an epsilon value) to allow nearly equal values to be considered sufficiently equal. RFX_Double and RFX_Single do not employ an epsilon value in their dirty field detection comparisons so they will occasionally mark fields whose values have undergone some form of rounding or truncation as being dirty. The process of transferring recordset member variables via DDX to and from the edit controls of a record view or a dialog box can introduce sufficient rounding errors to result in a field that is marked dirty. In addition, in Visual C++ 4.2 Microsoft Foundation Classes has been modified to use the "==" equality operator when comparing the cached and current values. The "==" invokes the Floating Point Unit and that, in itself, causes rounding that may result in the field being flagged dirty when it could actually be unchanged.
RESOLUTIONThere are several ways to address this problem:
MORE INFORMATION
Sample CodeThe following sample code is designed to replace RFX_Double for MFC 4.2. You can use this code as a guide for replacing RFX_Single and for making similar modifications to other versions of Microsoft Foundation Classes.
//**************************************************************** // rfx_double2.cpp // Most of this code is copied from the MFC source--see comments // for NEW code. // // .cpp file for version of RFX_Double that uses an epsilon value // in its dirty field comparison. // // THIS CODE IS WRITTEN TO WORK WITH MFC 4.2, TO WORK WITH OTHER // VERSIONS, THIS CODE MAY REQUIRE EXTENSIVE MODIFICATION. #include "stdafx.h" // NEW: Constant required for clean compile--different versions of MFC // use different constants.static const double afxDoublePseudoNull = AFX_RFX_DOUBLE_PSEUDO_NULL;
// NEW: Comparison that uses an epsilon to define a window within // which two values are considered equal.BOOL AFXAPI CompareValueWithEpsilon(void* pvSrc, void* pvDest, int nSrcType, double epsilon) { // Only call with type of double -- you can modify this to accept // other types. if (nSrcType != AFX_RFX_DOUBLE) { // called with unexpected type ASSERT(FALSE); return FALSE; } BOOL bDirty = FALSE; // Do dirty detection comparison factoring in epsilon value. if ((*(double *)pvDest) > ((*(double *)pvSrc) + epsilon) || (*(double *)pvDest) < ((*(double *)pvSrc) - epsilon)) bDirty = TRUE; return !bDirty;}
// Fourth parameter is new to this version--it allows you to specify // the width of the comparison window. // // NOTE: Epsilon is expected to be positive. void AFXAPI RFX_Double2(CFieldExchange* pFX, LPCTSTR szName, double& value, double epsilon){ ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange))); ASSERT(AfxIsValidString(szName)); switch (pFX->m_nOperation) { case CFieldExchange::MarkForUpdate: { // NEW: You must move this from original MFC location // to avoid problems with delegating in default case. UINT nField; if (!pFX->IsFieldType(&nField)) return; if (value != afxDoublePseudoNull) pFX->m_prs->ClearNullFieldStatus(nField - 1); // Get the field data, CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1]; // If the user changed the field value from previous value, // mark field dirty. if ((pInfo->m_bStatus & AFX_SQL_FIELD_FLAG_NULL)) { if (!pFX->m_prs->IsFieldStatusNull(nField - 1)) pFX->m_prs->SetDirtyFieldStatus(nField - 1); } else { // Saved field is not NULL. The current field is null, // so the field is dirty. BOOL bDirty = pFX->m_prs->IsFieldStatusNull(nField - 1); // If values differ, then field dirty void* pvDataCache = pInfo->m_pvDataCache; // NEW: Now call a function that does dirty field // detection based on an epsilon value. if (bDirty || !CompareValueWithEpsilon(&value, pvDataCache, pInfo->m_nDataType, epsilon)) pFX->m_prs->SetDirtyFieldStatus(nField - 1); }#ifdef _DEBUG // Field address must not change--ODBC's SQLBindCol depends // upon this. void* pvBind = &value; if (pInfo->m_pvBindAddress != pvBind) { TRACE1("Error: field address (column %u) has changed!\n", nField); ASSERT(FALSE); }#endif // _DEBUG
if ((pFX->m_pvField == NULL || pFX->m_pvField == &value) && pFX->m_prs->IsFieldStatusDirty(nField - 1)) { pFX->m_bField = TRUE; } break; } // NEW: Only process MarkForUpdate--delegate all else. default: { // NEW: Call the original RFX function for all // cases but MarkForUpdate. RFX_Double(pFX,szName, value); } return; }}
|
KBCategory: kbtshoot kbusage kbcode
© 1998 Microsoft Corporation. All rights reserved. Terms of Use. |