Ticket #41597

std::remquo does not yield the proper result for the quotient

Open Date: 2021-02-18 06:25 Last Update: 2021-03-20 19:07

Reporter:
Owner:
Type:
Status:
Closed
Component:
MileStone:
(None)
Priority:
5 - Medium
Severity:
5 - Medium
Resolution:
Fixed
File:
3
Vote
Score: 1
100.0% (1/1)
0.0% (0/1)

Details

Context : This problem was encountered with gcc on MinGW in the following version : gcc.exe (MinGW.org GCC-6.3.0-1) 6.3.0

Description :

The following code :

  1. double numer = 90.1 ;
  2. double denom = 90 .0 ;
  3. int quot ;
  4. double result = std::remquo(numer, denom, &quot) ;
  5. std::cout << "result " << result << std::endl ;
  6. std::cout << "quot " << quot << std::endl;
should yield :
result 0.1
quot 1

As is expected from std::remquo, quot has a magnitude which should be congruent (modulo 2 to the nth) to the magnitude of the integral quotient of x/y, n being greater or equal than 3.

However with gcc 6.3.0, on MinGW, the above instructions yield : result 0.1 quot 0

On another version of gcc (gcc 4.9.1 2014), the expected behavior is encountered.

Ticket History (3/19 Histories)

2021-02-18 06:25 Updated by: avhaecke
  • New Ticket "std::remquo does not yield the proper result for the quotient" created
2021-02-18 10:25 Updated by: apodtele
Comment

Ok. You should upgrade to GCC-9.2.0 and see if it is already fixed.

2021-02-21 02:56 Updated by: keith
  • Owner Update from (None) to keith
  • Component Update from GCC to WSL
  • Details Updated
Comment

Thanks for bringing this to our attention; FWIW, I can reproduce this, cross-compiling with mingw32-gcc version 9.2.0 on GNU/Linux, and running under Wine.

It would have been helpful, (and you would have received a quicker response), if you had provided the requisite SSCCE ... your sample code was incomplete; notwithstanding, I have adapted your sample, (as you should have done):

  1. #include <cmath>
  2. #include <cstdlib>
  3. #include <iostream>
  4. extern "C" char *basename( char * );
  5. int main( int argc, char **argv )
  6. {
  7. if( argc != 3 )
  8. { std::cerr << "Usage: " << basename(*argv) << " dividend divisor" << std::endl;
  9. return 1;
  10. }
  11. int quotient;
  12. double divisor = std::strtod( argv[2], nullptr );
  13. double dividend = std::strtod( argv[1], nullptr );
  14. double remainder = std::remquo( dividend, divisor, &quotient );
  15. std::cout << dividend << " / " << divisor << " = " << quotient
  16. << " remainder " << remainder << std::endl;
  17. return 0;
  18. }

When I compile, and run this, I see (as you did):
$ mingw32-g++ t-remquo.cc
$ ./a.exe 90.1 90.0
90.1 / 90 = 0 remainder 0.1

Looking in the <cmath> header file, I see that GCC's __builtin_remquo() is substituted for the std::remquo() call, and looking in GCC's gcc/builtins.c suggests that this should be delegated to MPFR. However, a study of the generated assembly code, (with $ mingw32-g++ -S -masm=intel t-remquo.cc -o /dev/stdout | less) reveals that the call is actually delegated to the mingwrt remquo() function; thus, this is a MinGW runtime library issue, rather than a GCC issue.

I understand the purpose of the fprem1 loop, at the start of the remquo() implementation, and believe that it is correct. However, I cannot make any sense, at all, of the bit-twiddling logic which follows; it seems clear that it does not do the right thing. OTOH, if I reimplement the entire remquo() function, using MPFR, that does DTRT; alternatively, adding:

  1. #include <climits>
  2. double remquo( double x, double y, int *q )
  3. { double r = remainder( x, y );
  4. *q = (int)(fmod( ((x - r) / y), ((double)(INT_MAX) + 1.0) ));
  5. return r;
  6. }
into my t-remquo.cc, (below the original set of #include statements), this also appears to successfully work around the issue:
$ mingw32-g++ remquo.cc
$ ./a.exe 90.1 90.0
90.1 / 90 = 1 remainder 0.1

2021-02-21 03:05 Updated by: keith
Comment

Reply To apodtele

Ok. You should upgrade to GCC-9.2.0 and see if it is already fixed.

I agree that it would be prudent for the OP to upgrade to GCC-9.2.0. However, as I noted in my formal analysis of the issue, that isn't going to resolve this problem, because it is not a GCC issue; it is a MinGW runtime library issue, (although I don't know why GCC directs its __builtin_remquo() call to the MinGW remquo() implementation, rather than to its own MPFR-based implementation).

2021-02-22 21:54 Updated by: keith
  • File libmingwex.a (File ID: 6102) is attached
2021-02-22 22:14 Updated by: keith
Comment

I've prepared the attached tentative patch, to resolve this issue; it appears to DTRT, testing under Wine with your example values, (and a few other multiples of 90.0, for the dividend).

I've also attached an updated copy of libmingwex.a, with the patch applied. Please install it, in place of your existing libmingwex.a, test, and confirm that it resolves the issue for you.

2021-02-22 22:52 Updated by: keith
Comment

Reply To avhaecke

Context : This problem was encountered with gcc on MinGW in the following version : gcc.exe (MinGW.org GCC-6.3.0-1) 6.3.0
...
On another version of gcc (gcc 4.9.1 2014), the expected behavior is encountered.

Just to clarify: I assume that your GCC-4.9.1 is not a MinGW release? I can find no evidence of any legitimate MinGW GCC release, of that version; the only GCC-4.9.x release, which we did publish, was GCC-4.9.3, (and I assume, given the vintage of the remquo() implementation in libmingwex.a, that our GCC-4.9.3 release would have exhibited the same erroneous behaviour).

2021-02-22 23:51 Updated by: avhaecke
Comment

To answer Keith's question : Yes indeed, the version GCC-4.9.1 I mentioned in the original post was not a MinGW Release. It came (in 2014) from MinGW-build project as a binary package. You can still find it online on source forge with MinGW-w64 project at : https://sourceforge.net/projects/mingw-w64/files/ under : Home / Toolchains targetting Win64 / Personal Builds / mingw-builds / 4.9.1 / threads-posix/ seh I do not currently have access to the installation archive and cannot tell you more precisely which package it came from : I will be able to check next week.

Reply To keith

Reply To avhaecke

Context : This problem was encountered with gcc on MinGW in the following version : gcc.exe (MinGW.org GCC-6.3.0-1) 6.3.0
...
On another version of gcc (gcc 4.9.1 2014), the expected behavior is encountered.

Just to clarify: I assume that your GCC-4.9.1 is not a MinGW release? I can find no evidence of any legitimate MinGW GCC release, of that version; the only GCC-4.9.x release, which we did publish, was GCC-4.9.3, (and I assume, given the vintage of the remquo() implementation in libmingwex.a, that our GCC-4.9.3 release would have exhibited the same erroneous behaviour).

Reply To keith

Reply To avhaecke

Context : This problem was encountered with gcc on MinGW in the following version : gcc.exe (MinGW.org GCC-6.3.0-1) 6.3.0
...
On another version of gcc (gcc 4.9.1 2014), the expected behavior is encountered.

Just to clarify: I assume that your GCC-4.9.1 is not a MinGW release? I can find no evidence of any legitimate MinGW GCC release, of that version; the only GCC-4.9.x release, which we did publish, was GCC-4.9.3, (and I assume, given the vintage of the remquo() implementation in libmingwex.a, that our GCC-4.9.3 release would have exhibited the same erroneous behaviour).

2021-02-23 00:30 Updated by: avhaecke
Comment

Reply To keith

I've prepared the attached tentative patch, to resolve this issue; it appears to DTRT, testing under Wine with your example values, (and a few other multiples of 90.0, for the dividend). I've also attached an updated copy of libmingwex.a, with the patch applied. Please install it, in place of your existing libmingwex.a, test, and confirm that it resolves the issue for you.

Thank you for your patch (and your support).

However, I cannot link with your new libmingwex.a library as it appears to have the following symbols as Undefined (U) :

_imp____msvcrt_realloc
_imp____msvcrt_free

Those 2 symbols do not appear in the libmingwex library that came from the installer. As a consequence, when I try to link with the new libmingwex.a, I get the 3 following errors :

\home\keith\builds\mingw\mingw-wsl\build\mingwrt\..\..\mingwrt\mingwex\memalign.c : 645: undefined reference to '_imp____msvcrt_free'
\home\keith\builds\mingw\mingw-wsl\build\mingwrt\..\..\mingwrt\mingwex\memalign.c : 557: undefined reference to '_imp____msvcrt_realloc'
\home\keith\builds\mingw\mingw-wsl\build\mingwrt\..\..\mingwrt\mingwex\memalign.c : 582: undefined reference to '_imp____msvcrt_realloc'

(Edited, 2021-02-23 01:32 Updated by: keith)
2021-02-23 02:40 Updated by: keith
Comment

Reply To avhaecke

Reply To keith

I've prepared the attached tentative patch, to resolve this issue; it appears to DTRT, testing under Wine with your example values, (and a few other multiples of 90.0, for the dividend). I've also attached an updated copy of libmingwex.a, with the patch applied. Please install it, in place of your existing libmingwex.a, test, and confirm that it resolves the issue for you.

Thank you for your patch (and your support). However, I cannot link with your new libmingwex.a library as it appears to have the following symbols as Undefined (U) :

_imp____msvcrt_realloc
_imp____msvcrt_free
  Those 2 symbols do not appear in the libmingwex library that came from the installer.

Okay. Then your mingwrt/w32api installation is too old; did you follow apodtele's advice, and upgrade to GCC-9.2.0? In conjunction with that, you should also be using up-to-date versions of the MinGW runtime, and W32API libraries — it simply isn't practical for me to continue backporting to those versions of the same vintage as GCC-6.3.0; the attached replacement libmingwex.a is compatible with version 5.4.x runtime libraries, (and maybe also 5.3.x), but probably not with anything older.

Furthermore, if you are using a mingw-get installer which suggests that GCC-6.3.0 is the latest release, then it is still referring to the (defunct) SourceForge FRS; please refer to this mailing-list post, for advice on ensuring that you can continue to receive updates.

(Edited, 2021-02-23 03:10 Updated by: keith)
2021-02-24 21:24 Updated by: avhaecke
Comment

Reply To keith

Reply To avhaecke

Reply To keith

I've prepared the attached tentative patch, to resolve this issue; it appears to DTRT, testing under Wine with your example values, (and a few other multiples of 90.0, for the dividend). I've also attached an updated copy of libmingwex.a, with the patch applied. Please install it, in place of your existing libmingwex.a, test, and confirm that it resolves the issue for you.

Thank you for your patch (and your support). However, I cannot link with your new libmingwex.a library as it appears to have the following symbols as Undefined (U) :

{{{ _impmsvcrt_realloc _impmsvcrt_free }}}

  Those 2 symbols do not appear in the libmingwex library that came from the installer.

Okay. Then your mingwrt/w32api installation is too old; did you follow apodtele's advice, and upgrade to GCC-9.2.0? In conjunction with that, you should also be using up-to-date versions of the MinGW runtime, and W32API libraries — it simply isn't practical for me to continue backporting to those versions of the same vintage as GCC-6.3.0; the attached replacement libmingwex.a is compatible with version 5.4.x runtime libraries, (and maybe also 5.3.x), but probably not with anything older. Furthermore, if you are using a mingw-get installer which suggests that GCC-6.3.0 is the latest release, then it is still referring to the (defunct) SourceForge FRS; please refer to this mailing-list post, for advice on ensuring that you can continue to receive updates.

Thank you for pointing me to the correct repository. I performed the full upgrade to GCC 9.2.0 and replaced the libmingwex.a with the one you provided. It did correct the unexpected behavior I had encountered and my specific problem i.e. quo gets the correct value after the call std::remquo(x, y, quo) when x and y are of double type.

However, I had a test program for remquo that also tested the case when x and y were "long double" rather than double and it continues to yield unexpected result with the library you provided. The program is as follows :

  1. #include <iostream>
  2. #include <cmath>
  3. int main(void)
  4. {
  5. int i = 14449 ;
  6. long double numer = 90.0 ;
  7. long double demo = 90.0 ;
  8. // long double
  9. long double result = std::remquo(number, denom, &i) ;
  10. std::cout << result << " " << i << std::endl ;
  11. return 0 ;
  12. }

and I get as a result (with the new library) :

nan -2147483648

I do not know if this behavior is expected as the correction may be a partial patch but I wanted to bring it to your attention. Many thanks for your time. Alex

(Edited, 2021-02-25 04:58 Updated by: keith)
2021-02-25 05:28 Updated by: keith
  • File libmingwex.a (File ID: 6102) is deleted
2021-02-25 05:55 Updated by: keith
Comment

Reply To avhaecke

I performed the full upgrade to GCC 9.2.0 and replaced the libmingwex.a with the one you provided.
It did correct the unexpected behavior I had encountered and my specific problem i.e. quo gets the correct value after the call std::remquo(x, y, quo) when x and y are of double type. However, I had a test program for remquo that also tested the case when x and y were "long double" rather than double and it continues to yield unexpected result with the library you provided.
[...code snipped...]
and I get as a result (with the new library) :

nan -2147483648
  I do not know if this behavior is expected as the correction may be a partial patch but I wanted to bring it to your attention.

Thanks, Alex.

Ouch! GIGO. That behaviour definitely wasn't expected, but I'd made a copy-and-paste error ... copied the remquof() preamble to remquol() but neglected to change the argument type references from DWORD to TBYTE, so the FPU was attempting to load REAL4 arguments from stack offsets within a single REAL10 value, yielding obviously invalid input.

I've fixed it, and refreshed the attached libmingwex.a. It appears to work correctly now, for me, when I change the data types from double to long double, where appropriate in my test program, (and substitute std::strtold() for std::strtod()), but I'd appreciate if you'd like to test as well. I'll update the patch, to reflect the correction, shortly.

BTW, please wrap example code in formatted text block markup, to preserve layout. The OSDN site documentation tells you how, or you can see how I've modified your posts, if you select the "edit" option.

2021-02-25 06:15 Updated by: keith
Comment

Addendum To keith

I've fixed it, ... I'll update the patch, to reflect the correction, shortly.

Done. The difference, w.r.t. the preceding patch, is effectively:

  1. $ hg diff
  2. diff --git a/mingwrt/mingwex/math/remquo_generic.sx b/mingwrt/mingwex/math/remquo_generic.sx
  3. --- a/mingwrt/mingwex/math/remquo_generic.sx
  4. +++ b/mingwrt/mingwex/math/remquo_generic.sx
  5. @@ -90,12 +90,12 @@
  6. */
  7. .globl _remquol
  8. .def _remquol; .scl 2; .type 32; .endef
  9. _remquol:
  10. - fld DWORD ptr 4[esp] /* FPU TOS = x */
  11. - fld DWORD ptr 16[esp] /* FPU TOS = y, x */
  12. + fld TBYTE ptr 4[esp] /* FPU TOS = x */
  13. + fld TBYTE ptr 16[esp] /* FPU TOS = y, x */
  14. mov edx, DWORD ptr 28[esp] /* EDX = *q */
  15. /* Hand off the preloaded register set, to the shared computational
  16. * back-end routine, to...
  17. */

2021-02-26 18:09 Updated by: avhaecke
Comment

Reply To keith

Reply To avhaecke

I performed the full upgrade to GCC 9.2.0 and replaced the libmingwex.a with the one you provided.
It did correct the unexpected behavior I had encountered and my specific problem i.e. quo gets the correct value after the call std::remquo(x, y, quo) when x and y are of double type. However, I had a test program for remquo that also tested the case when x and y were "long double" rather than double and it continues to yield unexpected result with the library you provided.
[...code snipped...]
and I get as a result (with the new library) :

nan -2147483648
  I do not know if this behavior is expected as the correction may be a partial patch but I wanted to bring it to your attention.

Thanks, Alex.
Ouch! GIGO. That behaviour definitely wasn't expected, but I'd made a copy-and-paste error ... copied the remquof() preamble to remquol() but neglected to change the argument type references from DWORD to TBYTE, so the FPU was attempting to load REAL4 arguments from stack offsets within a single REAL10 value, yielding obviously invalid input.
I've fixed it, and refreshed the attached libmingwex.a. It appears to work correctly now, for me, when I change the data types from double to long double, where appropriate in my test program, (and substitute std::strtold() for std::strtod()), but I'd appreciate if you'd like to test as well. I'll update the patch, to reflect the correction, shortly. BTW, please wrap example code in formatted text block markup, to preserve layout. The OSDN site documentation tells you how, or you can see how I've modified your posts, if you select the "edit" option.

Thank you for your answer. I've tested your new library and indeed it works for my test cases. Everything seems fine as far as I can tell. Many thanks for your time ! Alex

PS : and sorry for all the mistakes I made in the posts !

(Edited, 2021-02-26 18:29 Updated by: keith)
2021-03-20 19:07 Updated by: keith
  • Status Update from Open to Closed
  • Resolution Update from None to Fixed
Comment

I committed #38a2db6, (and related #b3f510a), to resolve this issue. These changes will be included in the next mingwrt release.

Attachment File List

Edit

Please login to add comment to this ticket » Login