VarRound implementation

Fabian Cenedese Cenedese at indel.ch
Mon Feb 23 05:11:19 CST 2004


>be. You have 2 choices: try and match their exact algorithm (v. hard
>w/o reverse engineering) or use the dutch rounding method (this makes
>more sense IMO).

I couldn't find anything about "Dutch rounding". Isn't rounding exactly
defined? 0.1-0.4 round down -> 0.0, 0.5-0.9 round up -> 1.0?

>> And how should I round a currency? It's valid on Windows but I
>> couldn't interpret the results. 
>> Is this some kind of fixed comma type?
>
>The currency value is multiplied by 10000 and stored in an I8 (See
>the VarCy* functions). So it has 4 dp of precision. To round to:
>
>4 dp or more => no op.
>0 dp => / 10000, then * 10000
>1 dp => / 1000, then * 1000
>2 dp => / 100, then * 100
>3 dp => / 10, then * 10
>
>mults and divs should be done on the I8 directly (no float).

I think I did it like this now. Still the DECIMAL part is missing. The tests
in the test_VarRound are commented out as compares on floats are
quite useless. Unless somebody finds a better way to test if the rounding
worked ok I'll remove them (which would be a pity).

bye  Fabi



Index: wine/dlls/oleaut32/oleaut32.spec
===================================================================
RCS file: /home/wine/wine/dlls/oleaut32/oleaut32.spec,v
retrieving revision 1.64
diff -u -r1.64 oleaut32.spec
--- wine/dlls/oleaut32/oleaut32.spec    21 Jan 2004 22:24:08 -0000      1.64
+++ wine/dlls/oleaut32/oleaut32.spec    23 Feb 2004 10:46:42 -0000
@@ -170,7 +170,7 @@
 172 stdcall VarInt(ptr ptr)
 173 stdcall VarNeg(ptr ptr)
 174 stdcall VarNot(ptr ptr)
-175 stub VarRound # stdcall (ptr long ptr)
+175 stdcall VarRound(ptr long ptr)
 176 stdcall VarCmp(ptr ptr long long)
 177 stdcall VarDecAdd(ptr ptr ptr)
 178 stdcall VarDecDiv(ptr ptr ptr)
Index: wine/dlls/oleaut32/variant.c
===================================================================
RCS file: /home/wine/wine/dlls/oleaut32/variant.c,v
retrieving revision 1.87
diff -u -r1.87 variant.c
--- wine/dlls/oleaut32/variant.c        17 Feb 2004 20:25:41 -0000      1.87
+++ wine/dlls/oleaut32/variant.c        23 Feb 2004 10:46:43 -0000
@@ -3436,6 +3436,135 @@
 
     return hRet;
 }
+
+
+/**********************************************************************
+ *              VarRound [OLEAUT32.175]
+ *
+ * Perform a round operation on a variant.
+ *
+ * PARAMS
+ *  pVarIn  [I] Source variant
+ *  deci    [I] Number of decimals to round to
+ *  pVarOut [O] Destination for converted value
+ *
+ * RETURNS
+ *  Success: S_OK. pVarOut contains the converted value.
+ *  Failure: An HRESULT error code indicating the error.
+ *
+ * NOTES
+ *  - Floating point values are rounded to the desired number of decimals.
+ *  - Negative values are rounded nearer to zero (and not bigger in absolute).
+ *  - Some integer types are just copied to the return variable.
+ *  - Some other integer types are not handled and fail.
+ */
+HRESULT WINAPI VarRound(LPVARIANT pVarIn, int deci, LPVARIANT pVarOut)
+{
+    VARIANT varIn;
+    HRESULT hRet = S_OK;
+    float factor;
+
+    TRACE("(%p->(%s%s),%d)\n", pVarIn, debugstr_VT(pVarIn), debugstr_VF(pVarIn), deci);
+
+    switch (V_VT(pVarIn))
+    {
+    /* cases that fail on windows */
+    case VT_I1:
+    case VT_I8:
+    case VT_UI2:
+    case VT_UI4:
+       hRet = DISP_E_BADVARTYPE;
+       break;
+
+    /* cases just copying in to out */
+    case VT_UI1:
+       V_VT(pVarOut) = V_VT(pVarIn);
+       V_UI1(pVarOut) = V_UI1(pVarIn);
+       break;
+    case VT_I2:
+       V_VT(pVarOut) = V_VT(pVarIn);
+       V_I2(pVarOut) = V_I2(pVarIn);
+       break;
+    case VT_I4:
+       V_VT(pVarOut) = V_VT(pVarIn);
+       V_I4(pVarOut) = V_I4(pVarIn);
+       break;
+    case VT_NULL:
+       V_VT(pVarOut) = V_VT(pVarIn);
+       /* value unchanged */
+       break;
+
+    /* cases that change type */
+    case VT_EMPTY:
+       V_VT(pVarOut) = VT_I2;
+       V_I2(pVarOut) = 0;
+       break;
+    case VT_BOOL:
+       V_VT(pVarOut) = VT_I2;
+       V_I2(pVarOut) = V_BOOL(pVarIn);
+       break;
+    case VT_BSTR:
+        hRet = VarR8FromStr(V_BSTR(pVarIn), LOCALE_USER_DEFAULT, 0, &V_R8(&varIn));
+        if (FAILED(hRet))
+            break;
+        pVarIn = &varIn;
+        /* Fall through ... */
+
+    /* cases we need to do math */
+    case VT_R8:
+       if (V_R8(pVarIn)>0) {
+           V_R8(pVarOut)=floor(V_R8(pVarIn)*pow(10, deci)+0.5)/pow(10, deci);
+       } else {
+           V_R8(pVarOut)=ceil(V_R8(pVarIn)*pow(10, deci)-0.5)/pow(10, deci);
+       }
+       V_VT(pVarOut) = V_VT(pVarIn);
+       break;
+    case VT_R4:
+       if (V_R4(pVarIn)>0) {
+           V_R4(pVarOut)=floor(V_R4(pVarIn)*pow(10, deci)+0.5)/pow(10, deci);
+       } else {
+           V_R4(pVarOut)=ceil(V_R4(pVarIn)*pow(10, deci)-0.5)/pow(10, deci);
+       }
+       V_VT(pVarOut) = V_VT(pVarIn);
+       break;
+    case VT_DATE:
+       if (V_DATE(pVarIn)>0) {
+           V_DATE(pVarOut)=floor(V_DATE(pVarIn)*pow(10, deci)+0.5)/pow(10, deci);
+       } else {
+           V_DATE(pVarOut)=ceil(V_DATE(pVarIn)*pow(10, deci)-0.5)/pow(10, deci);
+       }
+       V_VT(pVarOut) = V_VT(pVarIn);
+       break;
+    case VT_CY:
+       if (deci>3)
+           factor=1;
+       else
+           factor=pow(10, 4-deci);
+
+       if (V_CY(pVarIn).int64>0) {
+           V_CY(pVarOut).int64=floor(V_CY(pVarIn).int64*factor)/factor;
+       } else {
+           V_CY(pVarOut).int64=ceil(V_CY(pVarIn).int64*factor)/factor;
+       }
+       V_VT(pVarOut) = V_VT(pVarIn);
+       break;
+
+    /* cases we don't know yet */
+    default:
+       FIXME("unimplemented part, V_VT(pVarIn) == 0x%X, deci == %d\n",
+               V_VT(pVarIn) & VT_TYPEMASK, deci);
+       hRet = DISP_E_BADVARTYPE;
+    }
+
+    if (FAILED(hRet))
+      V_VT(pVarOut) = VT_EMPTY;
+
+    TRACE("returning 0x%08lx (%s%s),%f\n", hRet, debugstr_VT(pVarOut),
+          debugstr_VF(pVarOut), V_VT(pVarOut) == VT_R4 ? V_R4(pVarOut):-1);
+
+    return hRet;
+}
+
 
 /**********************************************************************
  *              VarMod [OLEAUT32.154]
Index: wine/dlls/oleaut32/tests/vartest.c
===================================================================
RCS file: /home/wine/wine/dlls/oleaut32/tests/vartest.c,v
retrieving revision 1.23
diff -u -r1.23 vartest.c
--- wine/dlls/oleaut32/tests/vartest.c  6 Feb 2004 05:23:48 -0000       1.23
+++ wine/dlls/oleaut32/tests/vartest.c  23 Feb 2004 10:46:47 -0000
@@ -2548,6 +2548,129 @@
        "VarNeg: VT_CY wrong, hres=0x%lX\n", hres);
 }
 
+static HRESULT (WINAPI *pVarRound)(LPVARIANT,int,LPVARIANT);
+
+#define VARROUND(vt,val,deci,rvt,rval) V_VT(&v) = VT_##vt; V_##vt(&v) = val; \
+        memset(&vDst,0,sizeof(vDst)); hres = pVarRound(&v,deci,&vDst); \
+        ok(hres == S_OK && V_VT(&vDst) == VT_##rvt && V_##rvt(&vDst) == (rval), \
+        "VarRound: expected 0x0,%d,%d, got 0x%lX,%d,%d\n", VT_##rvt, (int)(rval), \
+        hres, V_VT(&vDst), (int)V_##rvt(&vDst))
+
+#define VARROUNDF(vt,val,deci,rvt,rval) V_VT(&v) = VT_##vt; V_##vt(&v) = val; \
+        memset(&vDst,0,sizeof(vDst)); hres = pVarRound(&v,deci,&vDst); \
+        ok(hres == S_OK && V_VT(&vDst) == VT_##rvt && V_##rvt(&vDst) == (rval), \
+        "VarRound: expected 0x0,%d,%f, got 0x%lX,%d,%f\n", VT_##rvt, rval, \
+        hres, V_VT(&vDst), V_##rvt(&vDst))
+
+static void test_VarRound(void)
+{
+    static const WCHAR szNumMin[] = {'-','1','.','4','5','\0' };
+    static const WCHAR szNum[] = {'1','.','4','5','\0' };
+    HRESULT hres;
+    VARIANT v, vDst;
+    DECIMAL *pdec = &V_DECIMAL(&v);
+    CY *pcy = &V_CY(&v);
+
+    CHECKPTR(VarRound);
+
+    /* first check valid integer types */
+    VARROUND(BOOL,VARIANT_TRUE,0,I2,-1);
+    VARROUND(BOOL,VARIANT_FALSE,0,I2,0);
+    VARROUND(BOOL,1,0,I2,1);
+    VARROUND(UI1,1,0,UI1,1);
+    VARROUND(UI1,254,0,UI1,254);
+    VARROUND(I2,-32768,0,I2,-32768);
+    VARROUND(I2,-1,0,I2,-1);
+    VARROUND(I2,1,0,I2,1);
+    VARROUND(I4,-((int)(~0u >> 1)) - 1,0,I4,-((int)(~0u >> 1)) - 1);
+    VARROUND(I4,-1,0,I4,-1);
+    VARROUND(I4,1,0,I4,1);
+
+
+    /* MSDN states that rounding of R4/R8 is dependent on the underlying
+     * bit pattern of the number and so is architecture dependent. So we
+     * can check if the dest type and hres are ok but not more.
+     */
+
+    VARROUNDF(R4,1.0,0,R4,1.0);
+    VARROUNDF(R4,-1.0,0,R4,-1.0);
+    VARROUNDF(R8,1.0,0,R8,1.0);
+    VARROUNDF(R8,-1.0,0,R8,-1.0);
+
+    /* floating point numbers aren't exactly equal and we can't just
+     * compare the first few digits.
+    todo_wine {
+        VARROUNDF(DATE,1.451,1,DATE,1.5);
+        VARROUNDF(DATE,-1.45,1,DATE,-1.4);
+        VARROUNDF(BSTR,(BSTR)szNumMin,1,R8,-1.40);
+        VARROUNDF(BSTR,(BSTR)szNum,1,R8,1.50);
+
+        VARROUNDF(R4,1.23456,0,R4,1.0);
+        VARROUNDF(R4,1.23456,1,R4,1.2);
+        VARROUNDF(R4,1.23456,2,R4,1.23);
+        VARROUNDF(R4,1.23456,3,R4,1.235);
+        VARROUNDF(R4,1.23456,4,R4,1.2346);
+        VARROUNDF(R4,-1.23456,0,R4,-1.0);
+        VARROUNDF(R4,-1.23456,1,R4,-1.2);
+        VARROUNDF(R4,-1.23456,2,R4,-1.23);
+        VARROUNDF(R4,-1.23456,3,R4,-1.235);
+        VARROUNDF(R4,-1.23456,4,R4,-1.2346);
+
+        VARROUNDF(R8,1.23456,0,R8,1.0);
+        VARROUNDF(R8,1.23456,1,R8,1.2);
+        VARROUNDF(R8,1.23456,2,R8,1.23);
+        VARROUNDF(R8,1.23456,3,R8,1.235);
+        VARROUNDF(R8,1.23456,4,R8,1.2346);
+        VARROUNDF(R8,-1.23456,0,R8,-1.0);
+        VARROUNDF(R8,-1.23456,1,R8,-1.2);
+        VARROUNDF(R8,-1.23456,2,R8,-1.23);
+        VARROUNDF(R8,-1.23456,3,R8,-1.235);
+        VARROUNDF(R8,-1.23456,4,R8,-1.2346);
+    }
+    */
+
+    V_VT(&v) = VT_EMPTY;
+    hres = pVarRound(&v,0,&vDst);
+    ok(hres == S_OK && V_VT(&vDst) == VT_I2 && V_I2(&vDst) == 0,
+        "VarRound: expected 0x0,%d,0 got 0x%lX,%d,%d\n", VT_EMPTY,
+        hres, V_VT(&vDst), V_I2(&vDst));
+
+    V_VT(&v) = VT_NULL;
+    hres = pVarRound(&v,0,&vDst);
+    ok(hres == S_OK && V_VT(&vDst) == VT_NULL,
+        "VarRound: expected 0x0,%d got 0x%lX,%d\n", VT_NULL, hres, V_VT(&vDst));
+
+    /* not yet implemented so no use testing yet
+    todo_wine {
+        V_VT(&v) = VT_DECIMAL;
+        pdec->u.s.sign = DECIMAL_NEG;
+        pdec->u.s.scale = 0;
+        pdec->Hi32 = 0;
+        pdec->u1.s1.Mid32 = 0;
+        pdec->u1.s1.Lo32 = 1;
+        hres = pVarRound(&v,0,&vDst);
+        ok(hres == S_OK && V_VT(&vDst) == VT_DECIMAL &&
+            V_DECIMAL(&vDst).u.s.sign == 0,
+            "VarRound: expected 0x0,%d,0x00, got 0x%lX,%d,%02x\n", VT_DECIMAL,
+            hres, V_VT(&vDst), V_DECIMAL(&vDst).u.s.sign);
+
+        pdec->u.s.sign = 0;
+        hres = pVarRound(&v,0,&vDst);
+        ok(hres == S_OK && V_VT(&vDst) == VT_DECIMAL &&
+            V_DECIMAL(&vDst).u.s.sign == DECIMAL_NEG,
+            "VarRound: expected 0x0,%d,0x7f, got 0x%lX,%d,%02x\n", VT_DECIMAL,
+            hres, V_VT(&vDst), V_DECIMAL(&vDst).u.s.sign);
+    }
+    */
+
+    V_VT(&v) = VT_CY;
+    pcy->int64 = 10000;
+    hres = pVarRound(&v,0,&vDst);
+    ok(hres == S_OK && V_VT(&vDst) == VT_CY && V_CY(&vDst).int64 == 10000,
+        "VarRound: VT_CY wrong, hres=0x%lX\n", hres);
+
+}
+
 START_TEST(vartest)
 {
   hOleaut32 = LoadLibraryA("oleaut32.dll");
@@ -2573,4 +2696,5 @@
   test_VarFix();
   test_VarInt();
   test_VarNeg();
+  test_VarRound();
 }





More information about the wine-devel mailing list