Hello Martins!
Its me again. Stuck on another floating point issue in x86 32 bit with eliminating the C Runtime. And yes, I'm still working on it. Been three months now.
Do you know anything about _ftol2 and _ftol2_sse? I'm getting unresolved externals on those in compiling my ActiveX Grid Control dll. I'm assuming they are abbreviations for float to long. Odd thing is, nowhere in my code are there any four byte floats declared or used.
Here's the deal. My ActiveX Grid Control compiles/links/runs perfectly in x64. But in x86 its giving me those linker errors. The code is heavy with #ifdef Debug conditionals where I output debugging information to a log file, so I know exactly right down to the very statement what's causing the problem and errors. There really isn't much floating point math in the grid. Originally when I wrote the code there wasn't any. But couple years after that I got turned on to the necessity of writing "High DPI Aware" code, so that if the user changed Display or Display Resolution settings in Control Panel, my screens wouldn't look like s***. So to code that I needed about a dozen more lines of code and I have a situation where a double gets multiplied by an int and the result stored in a Windows DWORD. The double is a DPI scaling factor which commonly takes on values of 1.0, 1.25, or 1.5. I suppose other values are possible, but I believe those are the only values I’ve ever seen in playing with those settings in Control Panel on my specific laptop. The thought occurred to me that perhaps because the values are so simple and easily expressible in a four byte float that might be why the compiler is using _ftol2 instead of _dtoui which we’ve previously dealt with. This is actually the block of code from the grid dll where the doubles are declared and initialized…
| // DPI Handling
double dpiX, dpiY;
double rxRatio, ryRatio;
hDC = GetDC(NULL);
dpiX=GetDeviceCaps(hDC, LOGPIXELSX);
dpiY=GetDeviceCaps(hDC, LOGPIXELSY);
rxRatio=(dpiX/96);
ryRatio=(dpiY/96);
|
Its those rxRatio/ryRatio variables that take on such values as 1.0, 1.25, 1.5, etc. The way I use them is that they need to be multiplied against everything in an app that specifies the sizes of anything, such as the x, y, cx, and cy variables in CreateWindowEx() calls to create and position objects. For example, say you wanted a top level window at 75, 75 on the desktop that was 320 pixels wide and 300 pixels high…
| hWnd=CreateWindowEx(0, szClassName, szClassName, WS_OVERLAPPEDWINDOW, 75, 75, 320, 300, HWND_DESKTOP, 0, hIns, 0);
|
What you would do after obtaining the above DPI values would be to multiply all those numbers by rxRatio or ryRatio, as the case may be. However, I came up with a better solution that just uses a macro to do that as follows…
| #define SizX(x) x * rxRatio
#define SizY(y) y * ryRatio
…so the above CreateWindowEx() call becomes even simpler…
[code]
hWnd=CreateWindowEx(0, szClassName, szClassName, WS_OVERLAPPEDWINDOW, SizX(75), SizY(75), SizX(320), SizY(300), HWND_DESKTOP, 0, hIns, 0);
|
That’s what’s failing in the 32 bit builds and generating the linker errors. I have been trying to solve it using the techniques you showed me about a month or so ago when I was fighting with that _dtoui3 thingie. If you recall, to solve that problem I created this function…
| #ifdef _M_IX86
unsigned int __cdecl DoubleToU32(double x)
{
return (unsigned int)_mm_cvttsd_si32(_mm_set_sd(x));
}
#endif
|
…and its use in 32 bit builds was as follows in peeling off digits of a double to convert to a character string…
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | while(k<=17)
{
if(k == i)
k++;
*(p1+k)=48+(char)n;
x=x*10;
#ifdef _M_IX86
n=DoubleToU32(x);
#else
n = (size_t)x;
#endif
x = x-n;
k++;
}
|
So first thing I did was to try to use code like that in ftol2 and ftol2_sse implementations wrapped in extern “C”’s to see if that would link….
| extern "C" long __cdecl _ftol2_sse(double x)
{
return _mm_cvtsd_si32(_mm_set_sd(x));
}
extern "C" long __cdecl _ftol2(double x)
{
return _mm_cvtsd_si32(_mm_set_sd(x));
}
|
Note I used the versions that round instead of truncate. That solved the unresolved externals and the code built. But it doesn’t work. Doesn’t crash or anything; just doesn’t work. The result of the multiplication ends up being zero. I looked around the long list of compiler intrinsics to see if I could find anything better, and I did, so I tried this….
| extern "C" long _ftol2(float x)
{
return _mm_cvtss_si32(_mm_set_ss(x));
}
extern "C" long _ftol2_sse(float x)
{
return _mm_cvtss_si32(_mm_set_ss(x));
}
|
That didn’t work either. I’m some confused about what’s going on because the dll code that won’t work seems similar to exe code that does work. When I saw what was happening I decided to make a small exe test program to see if, in 32 bit, one could declare a double and assign a number to it, multiply that by an int, and assign the result to a DWORD…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | // cl StrTst5.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib
#include <windows.h>
#include "stdlib.h"
#include "stdio.h"
extern "C" int _fltused=1;
#define SizX(x) x * rxRatio
int main()
{
double rxRatio = 1.25;
int iColWidths = 110;
DWORD pColWidths;
pColWidths = SizX(iColWidths);
printf("pColWidths = %u\n",pColWidths);
getchar();
return 0;
}
|
...and that works perfectly. The result is 137...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | C:\Code\VStudio\Grids\x86>cl StrTst5.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
StrTst5.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation. All rights reserved.
/out:StrTst5.exe
TCLib.lib
kernel32.lib
StrTst5.obj
C:\Code\VStudio\Grids\x86>StrTst5
pColWidths = 137
|
Now admittedly, the code in the dll is a lot more complicated, but its unclear to me how that would make any difference, as the fundamental operation taking place is no different than above. What I mean by 'complicated' in the dll is that it has to handle 'instance data' and multiple instantiations of grids correctly, so everything is done through dynamic memory allocations and pointers. The actual statement from the grid code that's failing in 32 bit is this...
| pGridData2->pColWidths[i] = SizX(strFieldData[0].iVal()); // <<< Line Causing Problems!!!
|
The variable pGridData is a pointer to a GridData object, which is dynamically allocated for each grid, and its where I hang pointers to all the grid's private 'instance' data. One of the members is a pointer to another memory block where I store all the column widths specified by the user when the grid is instantiated. These are modifiable at run time by the user through dragging the column dividers with the mouse. That's the GridData->pColWidths[] member, which is typed as DWORDs. The SizX() I've already described. The strFieldData[0].iVal() term is a member function call on my String Class where I'm extracting the column widths from the grid setup string passed in by the user/client and converting them to ints. So yes, that's complicated, but fundamentally no different than a multiplication of a double by an int with the rounded result going to a DWORD. And that appears to be where _ftol2 and _ftol2_sse enter the picture somehow. What do you think Martins? Any ideas how I might solve this???
By the way, by removing the High DPI Aware code, which only amounts to little more than I've shown above, the grid buulds and runs fine - just like its x64 counterpart.