Alex Villací­s Lasso escribió:
Alexandre Julliard escribió:
Alex Villací­s Lasso <[EMAIL PROTECTED]> writes:

This patch added the setlocale(LC_ALL,  "") line to
dlls/kernel32/locale.c . The oleaut32 tests for vartype.c have been
failing since that time on non-English locales. I see now that setting
the locale around calls of sprintfW() is not thread-safe. However, I
think there is no big problem on going through a string in order to
convert a floating-point number into a DECIMAL, since otherwise, we
would need to implement our own bit-splicer for floating-point
numbers. As long as the starting locale for sprintfW() is known to be
the LC_NUMERIC="C", all other parsing should work as before the MacOS
patch. Therefore I propose the attached patch. This patch simply sets
setlocale(LC_NUMERIC, "C") at the end of LOCALE_Init() in
dlls/kernel32/locale.c in order to guarantee that sprintfW will always
use periods as decimal separators.

I put this in for now, but oleaut32 should really be fixed, we can't
force the whole process to format number in English just because
oleaut32 is broken. It doesn't matter too much for Wine itself, but
any Unix library that we load should be able to behave properly
according to the locale that the user has selected.

Well, here is an attempt to fix part of the breakage in oleaut32 that ties it to LC_NUMERIC being "C". This patch decomposes floats and doubles into the component bitfields, then copies the values into DECIMAL structures and manipulates them to get the corresponding DECIMAL value. This removes the step of converting the floating-point number into a string and therefore eliminates two uses of sprintfW on floats. This particular patch passes all tests, but I am not really sure about the rounding - I might be overdoing it. Please comment.

Changelog:
* Remove uses of sprintfW to convert floats into DECIMAL by directly parsing the floating-point representation.

Alex Villacís Lasso
Resending since there was no response since Monday.

--
The following cryptic message was allegedly found in the inner edge of a Windows
XP installation CD:

4F6E65204F5320746F2072756C65207468656D20616C6C2C204F6E65204F5320746F2066696E6420
7468656D2C0D0A4F6E65204F5320746F206272696E67207468656D20616C6C20616E6420696E2074
6865206461726B6E6573732062696E64207468656D2E0A

It is rumored that only a true Unix Wizard can decypher this mysterious message,
which supposedly encodes the true nature and purpose of the software.

--- wine-0.9.27-cvs/dlls/oleaut32/vartype.c	2006-12-07 13:38:15.000000000 -0500
+++ wine-0.9.27-cvs-patch/dlls/oleaut32/vartype.c	2006-12-10 16:30:02.000000000 -0500
@@ -4143,6 +4143,21 @@
 
 #define LOCALE_EN_US		(MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT))
 
+/* internal representation of the value stored in a DECIMAL. The bytes are
+   stored from LSB at index 0 to MSB at index 11
+ */
+typedef struct DECIMAL_internal
+{
+    DWORD bitsnum[3];  /* 96 significant bits, unsigned */
+    unsigned char scale;      /* number scaled * 10 ^ -(scale) */
+    unsigned int  sign : 1;   /* 0 - positive, 1 - negative */
+} VARIANT_DI;
+
+static HRESULT VARIANT_DI_FromR4(float source, VARIANT_DI * dest);
+static HRESULT VARIANT_DI_FromR8(double source, VARIANT_DI * dest);
+static void VARIANT_DIFromDec(const DECIMAL * from, VARIANT_DI * to);
+static void VARIANT_DecFromDI(VARIANT_DI * from, DECIMAL * to);
+
 /************************************************************************
  * VarDecFromR4 (OLEAUT32.193)
  *
@@ -4157,10 +4172,12 @@
  */
 HRESULT WINAPI VarDecFromR4(FLOAT fltIn, DECIMAL* pDecOut)
 {
-  WCHAR buff[256];
-
-  sprintfW( buff, szFloatFormatW, fltIn );
-  return VarDecFromStr(buff, LOCALE_EN_US, LOCALE_NOUSEROVERRIDE, pDecOut);
+  VARIANT_DI di;
+  HRESULT hres;
+  
+  hres = VARIANT_DI_FromR4(fltIn, &di);
+  if (hres == S_OK) VARIANT_DecFromDI(&di, pDecOut);
+  return hres;
 }
 
 /************************************************************************
@@ -4177,10 +4194,12 @@
  */
 HRESULT WINAPI VarDecFromR8(double dblIn, DECIMAL* pDecOut)
 {
-  WCHAR buff[256];
-
-  sprintfW( buff, szDoubleFormatW, dblIn );
-  return VarDecFromStr(buff, LOCALE_EN_US, LOCALE_NOUSEROVERRIDE, pDecOut);
+  VARIANT_DI di;
+  HRESULT hres;
+  
+  hres = VARIANT_DI_FromR8(dblIn, &di);
+  if (hres == S_OK) VARIANT_DecFromDI(&di, pDecOut);
+  return hres;
 }
 
 /************************************************************************
@@ -4596,16 +4615,6 @@
   return hRet;
 }
 
-/* internal representation of the value stored in a DECIMAL. The bytes are
-   stored from LSB at index 0 to MSB at index 11
- */
-typedef struct DECIMAL_internal
-{
-    DWORD bitsnum[3];  /* 96 significant bits, unsigned */
-    unsigned char scale;      /* number scaled * 10 ^ -(scale) */
-    unsigned int  sign : 1;   /* 0 - positive, 1 - negative */
-} VARIANT_DI;
-
 /* translate from external DECIMAL format into an internal representation */
 static void VARIANT_DIFromDec(const DECIMAL * from, VARIANT_DI * to)
 {
@@ -5190,6 +5199,284 @@
     return r_overflow;
 }
 
+/* This procedure receives a VARIANT_DI with a defined mantissa and sign, but
+   with an undefined scale, which will be assigned to (if possible). It also
+   receives an exponent of 2. This procedure will then manipulate the mantissa
+   and calculate a corresponding scale, so that the exponent2 value is assimilated
+   into the VARIANT_DI and is therefore no longer necessary. Returns S_OK if
+   successful, or DISP_E_OVERFLOW if the represented value is too big to fit into
+   a DECIMAL. */
+static HRESULT VARIANT_DI_normalize(VARIANT_DI * val, int exponent2, int isDouble)
+{
+    HRESULT hres = S_OK;
+    int exponent5, exponent10;
+    
+    /* A factor of 2^exponent2 is equivalent to (10^exponent2)/(5^exponent2), and
+       thus equal to (5^-exponent2)*(10^exponent2). After all manipulations, 
+       exponent10 might be used to set the VARIANT_DI scale directly. However,
+       the value of 5^-exponent5 must be assimilated into the VARIANT_DI. */
+    exponent5 = -exponent2;
+    exponent10 = exponent2;
+
+    /* Handle exponent5 > 0 */    
+    while (exponent5 > 0) {
+        char bPrevCarryBit;
+        char bCurrCarryBit;
+
+        /* In order to multiply the value represented by the VARIANT_DI by 5, it
+           is best to multiply by 10/2. Therefore, exponent10 is incremented, and
+           somehow the mantissa should be divided by 2.  */
+        if ((val->bitsnum[0] & 1) == 0) {
+            /* The mantissa is divisible by 2. Therefore the division can be done
+               without losing significant digits. */
+            exponent10++; exponent5--;
+            
+            /* Shift right */
+            bPrevCarryBit = val->bitsnum[2] & 1;
+            val->bitsnum[2] >>= 1;
+            bCurrCarryBit = val->bitsnum[1] & 1;
+            val->bitsnum[1] = (val->bitsnum[1] >> 1) | (bPrevCarryBit ? 0x80000000 : 0);
+            val->bitsnum[0] = (val->bitsnum[0] >> 1) | (bCurrCarryBit ? 0x80000000 : 0);
+        } else {        
+            /* The mantissa is NOT divisible by 2. Therefore the mantissa should 
+               be multiplied by 5, unless the multiplication overflows. */
+            DWORD temp_bitsnum[3];
+
+            exponent5--;
+            
+            memcpy(temp_bitsnum, val->bitsnum, 3 * sizeof(DWORD));
+            if (0 == VARIANT_int_mulbychar(temp_bitsnum, 3, 5)) {
+                /* Multiplication succeeded without overflow, so copy result back
+                   into VARIANT_DI */
+                memcpy(val->bitsnum, temp_bitsnum, 3 * sizeof(DWORD));
+
+                /* Mask out 3 extraneous bits introduced by the multiply */
+            } else {
+                /* Multiplication by 5 overflows. The mantissa should be divided
+                   by 2, and therefore will lose significant digits. */
+                exponent10++;
+
+                /* Shift right */
+                bPrevCarryBit = val->bitsnum[2] & 1;
+                val->bitsnum[2] >>= 1;
+                bCurrCarryBit = val->bitsnum[1] & 1;
+                val->bitsnum[1] = (val->bitsnum[1] >> 1) | (bPrevCarryBit ? 0x80000000 : 0);
+                val->bitsnum[0] = (val->bitsnum[0] >> 1) | (bCurrCarryBit ? 0x80000000 : 0);
+            }
+        }
+    }
+    
+    /* Handle exponent5 < 0 */
+    while (exponent5 < 0) {
+        /* In order to divide the value represented by the VARIANT_DI by 5, it 
+           is best to multiply by 2/10. Therefore, exponent10 is decremented, 
+           and the mantissa should be multiplied by 2 */
+        if ((val->bitsnum[2] & 0x80000000) == 0) {
+            /* The mantissa can withstand a shift-left without overflowing */
+            exponent10--; exponent5++;
+            VARIANT_int_shiftleft(val->bitsnum, 3, 1);            
+        } else {
+            /* The mantissa would overflow if shifted. Therefore it should be
+               directly divided by 5. This will lose significant digits, unless
+               by chance the mantissa happens to be divisible by 5 */
+            exponent5++;
+            VARIANT_int_divbychar(val->bitsnum, 3, 5);
+        }
+    }
+
+    /* At this point, the mantissa has assimilated the exponent5, but the 
+       exponent10 might not be suitable for assignment. The exponent10 must be
+       in the range [-DEC_MAX_SCALE..0], so the mantissa must be scaled up or
+       down appropriately. */
+    while (hres == S_OK && exponent10 > 0) {
+        /* In order to bring exponent10 down to 0, the mantissa should be 
+           multiplied by 10 to compensate. If the exponent10 is too big, this 
+           will cause the mantissa to overflow. */
+        if (0 == VARIANT_int_mulbychar(val->bitsnum, 3, 10)) {
+            exponent10--;
+        } else {
+            hres = DISP_E_OVERFLOW;
+        }
+    }
+    while (exponent10 < -DEC_MAX_SCALE) {
+        int rem10;
+        /* In order to bring exponent up to -DEC_MAX_SCALE, the mantissa should
+           be divided by 10 to compensate. If the exponent10 is too small, this
+           will cause the mantissa to underflow and become 0 */
+        rem10 = VARIANT_int_divbychar(val->bitsnum, 3, 10);
+        exponent10++;
+        if (VARIANT_int_iszero(val->bitsnum, 3)) {
+            /* Underflow, unable to keep dividing */
+            exponent10 = 0;
+        } else if (rem10 >= 5) {
+            DWORD x = 1;
+            VARIANT_int_add(val->bitsnum, 3, &x, 1);
+        }
+    }
+    /* This step is requierd in order to remove excess bits of precision from the 
+       end of the bit representation, down to the precision guaranteed by the
+       floating point number. */
+    if (isDouble) {
+        while (exponent10 < 0 && (val->bitsnum[2] != 0 || (val->bitsnum[2] == 0 && (val->bitsnum[1] & 0xFFE00000) != 0))) {
+            int rem10;
+            
+            rem10 = VARIANT_int_divbychar(val->bitsnum, 3, 10);
+            exponent10++;
+            if (rem10 >= 5) {
+                DWORD x = 1;
+                VARIANT_int_add(val->bitsnum, 3, &x, 1);
+            }
+        }
+    } else {
+        while (exponent10 < 0 && (val->bitsnum[2] != 0 || val->bitsnum[1] != 0 || 
+            (val->bitsnum[2] == 0 && val->bitsnum[1] == 0 && (val->bitsnum[0] & 0xFF000000) != 0))) {
+            int rem10;
+            
+            rem10 = VARIANT_int_divbychar(val->bitsnum, 3, 10);
+            exponent10++;
+            if (rem10 >= 5) {
+                DWORD x = 1;
+                VARIANT_int_add(val->bitsnum, 3, &x, 1);
+            }
+        }
+    }
+    /* Remove multiples of 10 from the representation */
+    while (exponent10 < 0) {
+        DWORD temp_bitsnum[3];
+
+        memcpy(temp_bitsnum, val->bitsnum, 3 * sizeof(DWORD));
+        if (0 == VARIANT_int_divbychar(temp_bitsnum, 3, 10)) {
+            exponent10++;
+            memcpy(val->bitsnum, temp_bitsnum, 3 * sizeof(DWORD));
+        } else break;        
+    }
+
+    /* Scale assignment */
+    if (hres == S_OK) val->scale = -exponent10;
+    
+    return hres;
+}
+
+typedef union
+{
+    struct
+    {
+        unsigned long m : 23;
+        unsigned int exp_bias : 8;
+        unsigned int sign : 1;
+    } i;
+    float f;
+} R4_FIELDS;
+
+/* Convert a 32-bit floating point number into a DECIMAL, without using an 
+   intermediate string step. */
+static HRESULT VARIANT_DI_FromR4(float source, VARIANT_DI * dest)
+{
+    HRESULT hres = S_OK;
+    R4_FIELDS fx;
+    
+    fx.f = source;
+
+    /* Detect special cases */
+    if (fx.i.m == 0 && fx.i.exp_bias == 0) {
+        /* Floating-point zero */
+        VARIANT_DI_clear(dest);
+    } else if (fx.i.m == 0  && fx.i.exp_bias == 0xFF) {
+        /* Floating-point infinity */
+        hres = DISP_E_OVERFLOW;
+    } else if (fx.i.exp_bias == 0xFF) {
+        /* Floating-point NaN */
+        hres = DISP_E_BADVARTYPE;
+    } else {
+        int exponent2;
+        VARIANT_DI_clear(dest);
+        
+        exponent2 = fx.i.exp_bias - 127;   /* Get unbiased exponent */
+        dest->sign = fx.i.sign;             /* Sign is simply copied */
+        
+        /* Copy significant bits to VARIANT_DI mantissa */
+        dest->bitsnum[0] = fx.i.m;
+        dest->bitsnum[0] &= 0x007FFFFF;
+        if (fx.i.exp_bias == 0) {
+            /* Denormalized number - correct exponent */
+            exponent2++;
+        } else {
+            /* Add hidden bit to mantissa */
+            dest->bitsnum[0] |= 0x00800000;
+        }
+
+        /* The act of copying a FP mantissa as integer bits is equivalent to
+           shifting left the mantissa 23 bits. The exponent2 is reduced to
+           compensate. */
+        exponent2 -= 23;
+        
+        hres = VARIANT_DI_normalize(dest, exponent2, 0);
+    }
+    
+    return hres;
+}
+
+typedef union
+{
+    struct
+    {
+        unsigned long m_lo : 32;    /* 52 bits of precision */
+        unsigned int m_hi : 20;
+        unsigned int exp_bias : 11; /* bias == 1023 */
+        unsigned int sign : 1;
+    } i;
+    double d;
+} R8_FIELDS;
+
+/* Convert a 64-bit floating point number into a DECIMAL, without using an 
+   intermediate string step. */
+static HRESULT VARIANT_DI_FromR8(double source, VARIANT_DI * dest)
+{
+    HRESULT hres = S_OK;
+    R8_FIELDS fx;
+    
+    fx.d = source;
+
+    /* Detect special cases */
+    if (fx.i.m_lo == 0 && fx.i.m_hi == 0 && fx.i.exp_bias == 0) {
+        /* Floating-point zero */
+        VARIANT_DI_clear(dest);
+    } else if (fx.i.m_lo == 0 && fx.i.m_hi == 0 && fx.i.exp_bias == 0x7FF) {
+        /* Floating-point infinity */
+        hres = DISP_E_OVERFLOW;
+    } else if (fx.i.exp_bias == 0x7FF) {
+        /* Floating-point NaN */
+        hres = DISP_E_BADVARTYPE;
+    } else {
+        int exponent2;
+        VARIANT_DI_clear(dest);
+        
+        exponent2 = fx.i.exp_bias - 1023;   /* Get unbiased exponent */
+        dest->sign = fx.i.sign;             /* Sign is simply copied */
+        
+        /* Copy significant bits to VARIANT_DI mantissa */
+        dest->bitsnum[0] = fx.i.m_lo;
+        dest->bitsnum[1] = fx.i.m_hi;
+        dest->bitsnum[1] &= 0x000FFFFF;
+        if (fx.i.exp_bias == 0) {
+            /* Denormalized number - correct exponent */
+            exponent2++;
+        } else {
+            /* Add hidden bit to mantissa */
+            dest->bitsnum[1] |= 0x00100000;
+        }
+
+        /* The act of copying a FP mantissa as integer bits is equivalent to
+           shifting left the mantissa 52 bits. The exponent2 is reduced to
+           compensate. */
+        exponent2 -= 52;
+        
+        hres = VARIANT_DI_normalize(dest, exponent2, 1);
+    }
+    
+    return hres;
+}
+
 /************************************************************************
  * VarDecDiv (OLEAUT32.178)
  *


Reply via email to