Community
cancel
Showing results for 
Search instead for 
Did you mean: 
firespot71
Beginner
267 Views

80-bit long double trouble

Hi,

Compiling a simple application with /Qlong-double (IA32, 12.1, integrated in MSVC on Win7 64-bit) and the two code lines below gives me a linker error on using std::numeric_limits<> when linking to multi-threaded debug DLLs (/MDd). Everything is fine if I link statically (/MTd). I suppose this is not intentional?


typedef long double real_type;
std::cout << std::numeric_limits::epsilon();

error LNK2019: unresolved external symbol "__declspec(dllimport) public: static UNKNOWN __cdecl std::numeric_limits::epsilon(void)" (__imp_?epsilon@?$numeric_limits@_T@std@@SA_TXZ)


Moreover, when using operator >> to read-in values with a long double variable as target, it reads rubbish or crashes.

real_type x;
std::cin >> x;
std::cout << x;

Entering '2.2' as input gives '-5.1488e-247' as output, which is quite different. I am not linking to any other file. Whether Intel links to a wrong MSVC-lib or not, I don't know, but I suppose Intel should get it right to linking to the correct lib as it has all the relevant info hany. Otherwise what runtime libs do I need to specify?


And finally: If linking to boost libraries, I strongly suppose that boost libraries must be built with the /Qlong-double option on to ensure binary compatibility - is that correct?


Any help appreciated !
Thanks.



0 Kudos
44 Replies
Georg_Z_Intel
Employee
230 Views

Hello,

AFAIK 80 bit long double support is not really supported in Microsoft Libraries - only very limited support is available:
http://msdn.microsoft.com/de-de/library/9cx8xs15.aspx

Please also refer to this thread here:
http://software.intel.com/en-us/forums/showthread.php?t=105429

Best regards,

Georg Zitzlsberger
SergeyKostrov
Valued Contributor II
230 Views

AFAIK 80 bit long double support is not really supported in Microsoft Libraries...

Georg,

The problem isnot related to 80-bit precision of the 'long double' type. User'firespot71' could not link his test-case and
it is related to some problem with STL. I'll follow up with more technical details.

Best regards,
Sergey
SergeyKostrov
Valued Contributor II
230 Views

Quoting firespot71
Any help appreciated !

In order tounderstand what is wrong you need to do independent verifications with CRT functions, like printf andscanf.

Here a couple of test-cases:

>> Test-Case #1 <<
...
int iMaxValue = std::numeric_limits< int >::max();
int fMaxValue = std::numeric_limits::max();
int dMaxValue = std::numeric_limits::max();
int ldMaxValue = std::numeric_limits::max();
...

>> Test-Case #2 <<
...
unsigned int uiControlWordx87 = 0UL;

//uiControlWordx87 = _control87( _PC_24, _MCW_PC );
//uiControlWordx87 = _control87( _PC_53, _MCW_PC );
//uiControlWordx87 = _control87( _PC_64, _MCW_PC );
uiControlWordx87 = _control87( _CW_DEFAULT, _MCW_PC );

printf( "Epsilon for float : %.16f\n", numeric_limits< float >::epsilon() );
printf( "Epsilon for double : %.32f\n", numeric_limits< double >::epsilon() );
printf( "Epsilon for long double : %.32f\n", numeric_limits< long double >::epsilon() );
...

>> Test-Case #3 ( Yourmodified test) <<
...
typedef long double real_type;

std::cout << "Test 1 - Epsilon for 'long double': " << numeric_limits< real_type >::epsilon() << endl;
printf( "Test 2 - Epsilon for 'long double': %.21f\n", numeric_limits< real_type >::epsilon() );

real_type x;

std::cout << "Enter a floating-point value: ";
std::cin >> x;

std::cout << x << endl;
printf( "Test 3 - Value for 'long double': %.21f\n", x );
...

SergeyKostrov
Valued Contributor II
230 Views

...
>> Test-Case #3 ( Yourmodified test) <<
...
typedef long double real_type;

std::cout << "Test 1 - Epsilon for 'long double': " << numeric_limits< real_type >::epsilon() << endl;
printf( "Test 2 - Epsilon for 'long double': %.21f\n", numeric_limits< real_type >::epsilon() );

real_type x;

std::cout << "Enter a floating-point value: ";
std::cin >> x;

std::cout << x << endl;
printf( "Test 3 - Value for 'long double': %.21f\n", x );
...


Here is output with Microsoft C++ compiler ( Visual Studio 2005 ):
...
Test 1 - Epsilon for 'long double': 2.22045e-016
Test 2 - Epsilon for 'long double': 0.000000000000000222045
Enter a floating-point value: 1.234567890
1.23457
Test 3 - Value for 'long double': 1.234567889999999900000
...

I don't see any problems.

Best regards,
Sergey

firespot71
Beginner
230 Views

Not working here.

Epsilon for long double: -1.#QNAN000000000000000000000000000
Test 1 - Epsilon for 'long double': -0
Test 2 - Epsilon for 'long double': 0.000000000000000000000
Enter a floating point-value: 2.2
-5.1488e-247
Test 3 - Value for 'long double': 0.000000000000000000000

This applies to debug mode (setting up a new console project app on MSVC9, leaving options by default and using C++ Compiler XE 12.1.5.344 (IA-32); the command line is:
/c /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /EHsc /RTC1 /MTd /GS /fp:fast /Fo"Debug/" /Fd"Debug/vc90.pdb" /W3 /nologo /ZI /Qlong-double

If I compile in release mode the command line is
/c /O2 /Oi /Qipo /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /EHsc /MD /GS /Gy /fp:fast /Fo"Release/" /Fd"Release/vc90.pdb" /W3 /nologo /Zi /Qlong-double
and results get even weirder as the first printf of numeric_limits works, Test 1 just outputs "-0", Test 2 produces a a ridiculously large ouput of rubbish digits spanning over 4 console lines, then cin and cout do work and output the correct number, and Test 3 outputes again ridiculously large rubbish of over four lines.

So ... ???

Bernard
Black Belt
230 Views

Maybe 'printf' is not able to properly format anddisplay long double values because of limited supportof 80-bit precision inMSVCRT library.As stated in MSDN articlelong double values are mapped to double 64-bitargumentsand somehow this mapping is not performed probably by 'printf' function.
As always the best option to understand what is going under the hood could be a reversing of library printf and scanf functions coupled with dynamic analysis under debugger.
TimP
Black Belt
230 Views

It's a bit strange that you would set /fp:fast when trying to reconcile Microsoft's treatment of long double against extended precision. As iliyapolak said, there is no support for long double in Microsoft printf or cout, so you will need to store all values in a std double to send to printf. You must change the contents of numeric_limits if you want to work this way; I don't remember you showing us what you did there.
firespot71
Beginner
230 Views

The /fp:fast was just left as by default for the testing purpose (this was merely a quickly set up dummy app to illustrate the problem!). Changing it to precise modes does not affect things.

It is unclear what you mean by changing contents numeric_limits. That's a std component and I don't intend to change there anything at all (I presume the implementation provides it to me as it must if conforming; indeed IIRC the standard I am not allowed to modify anything in namespace std).


Good, let's tackle this form a different, practical perspective:

Why does the debug DLL not provide the template specialization for long double, but the static does?

I am using only std C++. Why does the Intel compiler simply link to an MSVC runtime library that seems to be binary incompatible as you suggest? There are various alternatives (the best being simply that intel provides correct libs themselves, or provides a wrapper that converts long double to double prior to invoking msvc libs, to issuing a compile-time error if incompatibilities are detected, or simply disallowing the /Qlong-double option for Windoes at all). Frankly, just messing things up at runtime is not a particular good solution.

How am I supposed to know which parts of std C++ I may use and which not? That printf relies on some precompiled libs makes sense and is not difficult to guess. That std::numeric_limits seems to rely on some lib (see debug DLL issue) is much harder to guess as it could be easily implemented throughout a a plain header file. So using std functionalities seems risky whether they work or not. But how would I know whether ordinary maths ops like exp,log, or taking it to the extreme actually also even a plain + or *, would work correctly? After all, these could also link to an incompatible lib?

In summary, ss there any somewhat reliable use for long double on Windows at all?

thanks!
Georg_Z_Intel
Employee
230 Views

Hello,

we're re-using the system libraries from the different platforms for increased compatibility. It's extremely hard (practically almost impossible) to provide an own implementation with the very same semantics; also deviations from the standard & bugs need to be "emulated". That's tedious and expensive... and the benefit?

The downside is, however, that we depend on 3rd party implementations that might cause some head-scratching in rare cases. Apparently you found one of those.

Only Microsoft can answer the question why static libraries work here but DLLs don't. I guess they implemented it half-ways for some kind of internal testing... or realized that there were some glitches with DLLs and stopped any further implementation. Anyways, it's not there and won't ever be.

Bottom line is that even Microsoft guarantees the use of "long double" with a limited set of functions only (see my link above).

Why is it still there... the option?
Well, if you're writing a numerical library and really need 80 bit precision (internally) you still can do it. The option "/Qlong-double" is for the very few who implement such libraries.
We've lots of options where you can "shoot yourself into your own foot" if you're not exactly knowing the details behind the scenes. We provide them to provide maximum flexibility. They're not meant for day-to-day use.

Btw.: In our latest documentation "/Qlong-double" is not prominently documented (anymore). Only found two examples were we still use it for good reason.

I hope I clarified your concerns.

Best regards,

Georg Zitzlsberger
SergeyKostrov
Valued Contributor II
230 Views

Quoting firespot71
...
the command line is:
/c /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "
UNICODE" /EHsc /RTC1 /MTd /GS /fp:fast /Fo"Debug/" /Fd"Debug/vc90.pdb" /W3 /nologo /ZI /Qlong-double
...

Regarding a linking error. Please try to compile with '_MBCS' / 'MBCS'macrosinstead of '_UNICODE' / 'UNICODE' macros. I had lots of
similar problems with STL and new C++ operators when I was trying to compile with 'UNICODE' defined.

Best regards,
Sergey
firespot71
Beginner
230 Views

Hi,

The _MBCS / MBCS does not solve the linker problem, unfortunately.

OK Georg I understand you by now, so the functions stated in your link are the _only_ ones that I can expect to safely use with 80-bit long double out of everything that is provided in the whole of Standard C++ (I assume I may, of course, also use built-in operators such as +,-,*,/, <, >= etc.); for anything else all bets are off. Hm, quite restrictive.

cheers
SergeyKostrov
Valued Contributor II
230 Views

Here is another set of results from MinGW and Borland C++ compilers:

Application - MgwTestApp - WIN32_MGW
Tests: Start
> Test1017 Start <
Sub-Test 48
Test 1 - Epsilon for 'long double': 1.0842e-019
Test 2 - Epsilon for 'long double': 0.000000000000000000000
Enter a floating-point value: 1.234567890
1.23457
Test 3 - Value for 'long double': -0.000000000000000000000
> Test1017 End <
Tests: Completed

Application - BccTestApp - WIN32_BCC
Tests: Start
> Test1017 Start <
Sub-Test 48
Test 1 - Epsilon for 'long double': 1.0842e-19
Test 2 - Epsilon for 'long double': -0.000000000000000000000
Enter a floating-point value: 1.234567890
1.23457
Test 3 - Value for 'long double': -0.000000000000000000000
> Test1017 End <
Tests: Completed

As you can see in both casesthe C++ operator >> returned a correct roundedvalue'1.23457'.

Sorry guys, butI don't understand the point of all these fuzzy explanations from Georg. Three C++ compilers,
that is Microsoft, MinGW and Borland,passed the test and only Intel C++ compiler failed.

Best regards,
Sergey

Georg_Z_Intel
Employee
230 Views

Hello Sergey,

did you check the size of "long double"? It's still 64 bit for the Microsoft Visual Studio* compiler (for the others I assume that's also true). AFAIK you cannot change it to 80 bit unless you're using some old 16 bit versions...?

From the Microsoft Visual Studio* documentation:
Type longdouble is a floating type that is equal to type double.

So, what you're testing is 64 bit FP (type "double").

Best regards,

Georg Zitzlsberger
SergeyKostrov
Valued Contributor II
230 Views

Hello Sergey,

did you check the size of "long double"?...

Yes, I did and I'll provide you with a report for 4 different C++ compilers. However, the problem is not related to
the double-precision data type 'long double'. There is a linker error described in the 1st post of the thread and by some
reason you're ignoring this. Am I wrong? Also, 'firespot71' had a problem with the C++ operator >> and you're ignoring this and
pressing that the problem is related to the 'long double'.

Georg, did you try to reproduce it? If Yes,did you try tostep into >> C++ operator in order to understand why some wrong value is returned?
Unfortunately, I can't explain what is wrong withthat really simple test case.

Guys, you're trying to blame Microsoft without a completedinvestigation on your side. Please, take a look at it and I appreciate your feedback.

I'll follow up some time later because we had apower outage and everybody is busy with recovering some lost pieces of data.

Best regards,
Sergey
Georg_Z_Intel
Employee
230 Views

Hello Sergey,

it works well if you omit the option "/Qlong-double". Then data types of "long double" will be 64 bit (double precision), same as for current Microsoft Visual Studio* compilers (and the others). That's the standard on the Windows* platform.

The option "/Qlong-double" is only available for the Intel C++ Compiler and not (well) documented. It extends the size of type "long double" to 80 bit (extended double precision) and hence conflicts with the ABI of existing libraries (some exceptions, though). That's why there are linker errors and your FP variables are not printed correctly.
The use of this option is neither required for using "long double" types, nor should it be used in normal applications. It's something like the options for changing the calling convention - they for sure can break things but can also be useful in very rare cases.

Hence, don't use "/Qlong-double" unless you have very good reasons.

Best regards,

Georg Zitzlsberger
TimP
Black Belt
230 Views

In particular, as you indicated your intention to use the unmodified Microsoft STL headers which support only 64-bit (53-bit precision) long double, you should heed Georg's advice.
Bernard
Black Belt
230 Views

IIRC cin and cout operators are linked against MSVCRT or MSVCP library which does not support long double precision. So this could be a reason for linker generated errors.
SergeyKostrov
Valued Contributor II
230 Views

Hi Iliya,

Quoting iliyapolak
IIRC cin and cout operators are linked against MSVCRT or MSVCP library which does not support long double precision. So this could be a reason for linker generated errors.


I verified it and please take a look at aPost #4.

Here is output with Microsoft C++ compiler ( Visual Studio 2005 ):
...
Test 1 - Epsilon for 'long double': 2.22045e-016
Test 2 - Epsilon for 'long double': 0.000000000000000222045
Enter a floating-point value: 1.234567890
1.23457
Test 3 - Value for 'long double': 1.234567889999999900000
...

Best regards,
Sergey

SergeyKostrov
Valued Contributor II
230 Views

...
it works well if you omit the option "/Qlong-double". Then data types of "long double" will be 64 bit (double precision), same as for current Microsoft Visual Studio* compilers (and the others). That's the standard on the Windows* platform.

[SergeyK] Thank you for the explanations.It would be nice toinvestigatewhy it createsthat problem. Aren'tIntel Software Engineersinterested in that?
I simply wanted to tell that if there is some problem with Intel C++ compiler when the option "/Qlong-double" is used and
it is not fixed or disabled completelythat"bad-piece-of-codes"looks like a "time bomb".

Best regards,
Sergey
SergeyKostrov
Valued Contributor II
43 Views

Hereare a couple of more things...

Quoting firespot71
...
typedef long double real_type;
std::cout << std::numeric_limits::epsilon();

error
LNK2019: unresolved external symbol "__declspec(dllimport) public: static UNKNOWN __cdecl std::numeric_limits<UNKNOWN>::epsilon(void)" (__imp_?epsilon@?$numeric_limits@_T@std@@SA_TXZ)

I've looked at 'limits' header file ( VS 2005 / \VC\Include folder )and this is how'numeric_limits' isdeclared:
[cpp]... // TEMPLATE CLASS numeric_limits template class numeric_limits : public _Num_base { // numeric limits for arbitrary type _Ty (say little or nothing) public: ... static _Ty __CRTDECL epsilon() _THROW0() { // return smallest effective increment from 1.0 return (_Ty(0)); } ... }; ...[/cpp]
My question is: Why Intel C++ compileruses 'UNKNOWN' data type when the 'real_type' ( 'long double' ) is explicitly declared?

Also, take a look inside of 'limits' header file and you will see thattemplate classesforthe following data types are declared:

...
// CLASS numeric_limits
// CLASS numeric_limits
// CLASS numeric_limits<_Bool>
// CLASS numeric_limits
// CLASS numeric_limits
// CLASS numeric_limits
// CLASS numeric_limits
// CLASS numeric_limits
// CLASS numeric_limits
// CLASS numeric_limits
// CLASS numeric_limits
// CLASS numeric_limits<_LONGLONG>
// CLASS numeric_limits<_ULONGLONG>
// CLASS numeric_limits
// CLASS numeric_limits
// CLASS numeric_limits
...

including 'long double' data type. Of course, there will bea linker error because there is nodeclaration for atemplate class 'numeric_limits'. Isn't that true?

Best regards,
Sergey
Reply