Today I discovered .. that the scanf family of functions was introducing float (32-bit floating-point) noise into double (64-bit floating-point) results.
I see it here, too:
$ sudo apt-get install mingw32 $ i586-mingw32msvc-gcc test_fscanf.c $ echo "1.1e+01 1.1e+00 1.1e-01 1.1e-02 1.1e-03 1.1e-04" | ~/wine-git/wine a.exe 1.10000000000000000e+001 1.10000000000000009e+000 1.10000001639127737e-001 1.10000003278255491e-002 1.10000004917383261e-003 1.10000006556511075e-004
You can work around it with 'winetricks vcrun6', after which the test program outputs: 1.10000000000000000e+001 1.10000000000000010e+000 1.10000000000000000e-001 1.09999999999999990e-002 1.10000000000000010e-003 1.10000000000000000e-004
So the problem is in wine's msvcrt.dll.
You could narrow it down further by printing out the raw byte strings for the numbers and then sprinking that printout through the path from when scanf is called to where printf returns. (Or, I suppose, step through it with a debugger instead.)
But the best place to report the bug is http://bugs.winehq.org - Dan
On 2011-08-30 08:33-0700 Dan Kegel wrote:
Today I discovered .. that the scanf family of functions was introducing float (32-bit floating-point) noise into double (64-bit floating-point) results.
I see it here, too:
$ sudo apt-get install mingw32 $ i586-mingw32msvc-gcc test_fscanf.c $ echo "1.1e+01 1.1e+00 1.1e-01 1.1e-02 1.1e-03 1.1e-04" | ~/wine-git/wine a.exe 1.10000000000000000e+001 1.10000000000000009e+000 1.10000001639127737e-001 1.10000003278255491e-002 1.10000004917383261e-003 1.10000006556511075e-004
You can work around it with 'winetricks vcrun6', after which the test program outputs: 1.10000000000000000e+001 1.10000000000000010e+000 1.10000000000000000e-001 1.09999999999999990e-002 1.10000000000000010e-003 1.10000000000000000e-004
So the problem is in wine's msvcrt.dll.
You could narrow it down further by printing out the raw byte strings for the numbers and then sprinking that printout through the path from when scanf is called to where printf returns. (Or, I suppose, step through it with a debugger instead.)
But the best place to report the bug is http://bugs.winehq.org
Hi Dan:
Thanks for these hints. It was good to know of that workaround involving downloading a Microsoft library to replace the one from wine in case I needed it. However, I prefer a direct fix if possible so I looked further and finally found the code in question in the _FUNCTION_ routine in wine-1.3.27/dlls/msvcrt/scanf.h. Sure enough it has an inappropriate float in it which appears to be the source of the trouble.
Therefore, I tried this patch:
--- scanf.h_original 2011-08-30 12:35:25.000000000 -0700 +++ scanf.h 2011-08-30 12:36:23.000000000 -0700 @@ -346,7 +346,7 @@ /* handle exponent */ if (width!=0 && (nch == 'e' || nch == 'E')) { int exponent = 0, negexp = 0; - float expcnt; + double expcnt; nch = _GETC_(file); if (width>0) width--; /* possible sign on the exponent */
rebuilt wine-1.3.27, and I now have this result:
bash.exe-3.1$ echo "1.1e+01 1.1e+00 1.1e-01 1.1e-02 1.1e-03 1.1e-04" | ./a.exe 1.10000000000000000e+001 1.10000000000000009e+000 1.10000000000000001e-001 1.10000000000000011e-002 1.10000000000000028e-003 1.10000000000000031e-004
Pretty good, if I do say so myself, for someone who has only a superficial understanding of the scanf code!
I will follow up with a proper bug report at http://bugs.winehq.org so this patch isn't lost.
But before I do that I am going to charge ahead with ephcom2 build and tests to make sure this scanf function family fix for wine-1.3.27 works in that much more complicated case as well.
Best wishes, and thanks again for the hints.
Alan
__________________________ Alan W. Irwin
Astronomical research affiliation with Department of Physics and Astronomy, University of Victoria (astrowww.phys.uvic.ca).
Programming affiliations with the FreeEOS equation-of-state implementation for stellar interiors (freeeos.sf.net); the Time Ephemerides project (timeephem.sf.net); PLplot scientific plotting software package (plplot.sf.net); the libLASi project (unifont.org/lasi); the Loads of Linux Links project (loll.sf.net); and the Linux Brochure Project (lbproject.sf.net). __________________________
Linux-powered Science __________________________
On 2011-08-30 13:25-0700 Alan W. Irwin wrote:
On 2011-08-30 08:33-0700 Dan Kegel wrote:
Today I discovered .. that the scanf family of functions was introducing float (32-bit floating-point) noise into double (64-bit floating-point) results.
I see it here, too:
$ sudo apt-get install mingw32 $ i586-mingw32msvc-gcc test_fscanf.c $ echo "1.1e+01 1.1e+00 1.1e-01 1.1e-02 1.1e-03 1.1e-04" | ~/wine-git/wine a.exe 1.10000000000000000e+001 1.10000000000000009e+000 1.10000001639127737e-001 1.10000003278255491e-002 1.10000004917383261e-003 1.10000006556511075e-004
You can work around it with 'winetricks vcrun6', after which the test program outputs: 1.10000000000000000e+001 1.10000000000000010e+000 1.10000000000000000e-001 1.09999999999999990e-002 1.10000000000000010e-003 1.10000000000000000e-004
So the problem is in wine's msvcrt.dll.
You could narrow it down further by printing out the raw byte strings for the numbers and then sprinking that printout through the path from when scanf is called to where printf returns. (Or, I suppose, step through it with a debugger instead.)
But the best place to report the bug is http://bugs.winehq.org
Hi Dan:
Thanks for these hints. It was good to know of that workaround involving downloading a Microsoft library to replace the one from wine in case I needed it. However, I prefer a direct fix if possible so I looked further and finally found the code in question in the _FUNCTION_ routine in wine-1.3.27/dlls/msvcrt/scanf.h. Sure enough it has an inappropriate float in it which appears to be the source of the trouble.
Therefore, I tried this patch:
--- scanf.h_original 2011-08-30 12:35:25.000000000 -0700 +++ scanf.h 2011-08-30 12:36:23.000000000 -0700 @@ -346,7 +346,7 @@ /* handle exponent */ if (width!=0 && (nch == 'e' || nch == 'E')) { int exponent = 0, negexp = 0;
float expcnt;
double expcnt; nch = _GETC_(file); if (width>0) width--; /* possible sign on the exponent */
rebuilt wine-1.3.27, and I now have this result:
bash.exe-3.1$ echo "1.1e+01 1.1e+00 1.1e-01 1.1e-02 1.1e-03 1.1e-04" | ./a.exe 1.10000000000000000e+001 1.10000000000000009e+000 1.10000000000000001e-001 1.10000000000000011e-002 1.10000000000000028e-003 1.10000000000000031e-004
Hi Dan:
Subsequently, I went a lot deeper into this because I had a more rigourous test application that showed the above patch (even if using the proper type of long double rather than double) is still not good enough. Details (including new patch and new test application) are at http://bugs.winehq.org/show_bug.cgi?id=28422.
Here is the unpatched and patched Wine result for the new test code for one input number:
bash.exe-3.1$ echo "1.1e-30" |./a.exe 1.1e-30 is input string 1.1000004917384256455392e-030 = 3f9b64f8772f16505258 is long double value 1.1000004917384256198806e-030 = 39b64f8772f16505 is double value 1.1000004917384e-030: 13: 1.1000009834770454030908e-030 = 39b64f881a45deaf 1.10000049173843e-030: 14: 1.1000009834770755310078e-030 = 39b64f881a45df5b 1.100000491738426e-030: 15: 1.1000009834770715022747e-030 = 39b64f881a45df44 1.1000004917384256e-030: 16: 1.1000009834770711519501e-030 = 39b64f881a45df42 1.10000049173842562e-030: 17: 1.1000009834770711519501e-030 = 39b64f881a45df42
bash.exe-3.1$ echo "1.1e-30" |./a.exe 1.1e-30 is input string 1.0999999999999999999835e-030 = 3f9b64f86cb9cefaf7a0 is long double value 1.0999999999999999165078e-030 = 39b64f86cb9cefaf is double value 1.1000000000000e-030: 13: 1.0999999999999999165078e-030 = 39b64f86cb9cefaf 1.10000000000000e-030: 14: 1.0999999999999999165078e-030 = 39b64f86cb9cefaf 1.100000000000000e-030: 15: 1.0999999999999999165078e-030 = 39b64f86cb9cefaf 1.0999999999999999e-030: 16: 1.0999999999999999165078e-030 = 39b64f86cb9cefaf 1.09999999999999992e-030: 17: 1.0999999999999999165078e-030 = 39b64f86cb9cefaf
The latter patched result is in exact agreement with the corresponding Linux result except for the 3-digit exponents.
As a result of this patch (to wine-1.3.26, but wine-1.3.28 has the same numerically imprecise scanf code so the patch will apply there as well), conversion of JPL ascii ephemeride data by the ephcom-2.0.2 software to double internal form for 1.5GBytes of doubles gives exact agreement between the Wine platform and Linux platform results for all but 0.04 per cent of the doubles Furthermore, those differing ones only disagree at the relative level of 1.e-16). This is the result you would expect if the Linux and (patched) Wine scanf flipped the last bit of the 80-bit long double number internal storage for the converted result in opposite ways most of the time. That bit flip would propagate to a differently rounded double result (which has 11 bits less in the hidden bit mantissa) roughly 0.04 per cent (~ 1/2^11) of the time in good agreement with what is observed. I am extremely happy with that detailed comparison which goes far beyond the single-number (or few number) comparisons you get with the above test routine.
I don't want to get much deeper involved in Wine development because I have my own free software projects (such as ephcom) to develop and release. But I do feel this is an extremely important bug fix for Wine's version of msvcrt.dll since the scanf family of functions is so fundamental. Therefore, I hope you will act as a conscience/fix advocate for the Wine development team to make sure this numerical precision fix for the scanf family of routines in msvcrt.dll gets into the next Wine release.
Alan __________________________ Alan W. Irwin
Astronomical research affiliation with Department of Physics and Astronomy, University of Victoria (astrowww.phys.uvic.ca).
Programming affiliations with the FreeEOS equation-of-state implementation for stellar interiors (freeeos.sf.net); the Time Ephemerides project (timeephem.sf.net); PLplot scientific plotting software package (plplot.sf.net); the libLASi project (unifont.org/lasi); the Loads of Linux Links project (loll.sf.net); and the Linux Brochure Project (lbproject.sf.net). __________________________
Linux-powered Science __________________________