NO

Author Topic: Complex Float gives surprising result  (Read 16105 times)

JohnF

  • Guest
Re: Complex Float gives surprising result
« Reply #15 on: August 15, 2008, 08:43:07 AM »
ofniw, how much of the complex math library do you need?

Whatever, you could roll out your own cmath lib. I have the code if you would like to put it in a DLL.

Instead of using 'float complex' one would use a struct with two floats as one of your examples used, which would make it compatible with your VB structs.

John

ofniw

  • Guest
Re: Complex Float gives surprising result
« Reply #16 on: August 15, 2008, 08:28:50 PM »
Dear all,
regardless what comes out of this, I thank you all very much for your engagement. It was a pleasure to discuss with you.

Also, maybe we could or can help somebody else, not to experience the same problem.

Dear JohnF,
special thanks for your kind offer. I will restrict myself to double precision. So additional procedures are not necessary.


Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: Complex Float gives surprising result
« Reply #17 on: August 17, 2008, 07:23:07 PM »
All the functions in the DLL were returning structs not pointers. So in this case the value/s in the struct is/are copied to the receiving var.
John

Sorry John, but Pelle changed the way it return structures, he always return a pointer to the structure, even when the structure size could fit in the register pair EAX-EDX. This diverges from M$ standard.
Considering this the use of a larger structure as double, or a filler, makes the returned convention adhering to M$ again (you can check the disassembly of routines).
That's why I was considering to declare a pointer returned. Is this declared as BYREF in VB?

P.S. Ofniw made check modifying my code, he still doesn't declared a structure pointer as returned value.
« Last Edit: August 17, 2008, 07:27:02 PM by frankie »
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

JohnF

  • Guest
Re: Complex Float gives surprising result
« Reply #18 on: August 17, 2008, 08:57:12 PM »
All the functions in the DLL were returning structs not pointers. So in this case the value/s in the struct is/are copied to the receiving var.
John
Sorry John, but Pelle changed the way it return structures, he always return a pointer to the structure, even when the structure size could fit in the register pair EAX-EDX. This diverges from M$ standard.
Considering this the use of a larger structure as double, or a filler, makes the returned convention adhering to M$ again (you can check the disassembly of routines).

Frankie, have you checked this? I'm pretty sure returning a struct is required by the standard. And looking at the disassembly of a test routine it looks like the struct is being returned. If I'm wrong I will apologise.

John

ofniw

  • Guest
Re: Complex Float gives surprising result
« Reply #19 on: August 19, 2008, 11:24:41 PM »
Good evening gentlemen,

unfortunately the discussion now is so deep into the details, that I don't understand much.

However as an "external" observer I have the feeling, that it may go into a wrong direction. Maybe it helps, if I sum up a few facts since the beginning of this thread.

1. Calling the function from VB6 and passing of structures


The definition of the call is:

Declare Function c_add_c Lib ".\simpdll\SimpDLL.DLL" Alias "c_add" (a As Complex, c As Complex) As Complex

This is equivalent to
Declare Function c_add_c Lib ".\simpdll\SimpDLL.DLL" Alias "c_add" (Byref a As Complex, Byref c As Complex) As Complex
because in VB6 byref is the default..

The definition
Declare Function c_add_c Lib ".\simpdll\SimpDLL.DLL" Alias "c_add" (Byval a As Complex, Byvalc As Complex) As Complex
is rejected by the VB6 compiler with an error message, that this is not allowed.

So it is granted, that VB6 passes a pointer to the structure.

1.1 Using the C-debugger, it was found, that these pointers obviously are not interpreted correctly.

Redefining the structure to a ComplexX-Structure with the filler constant, solved this problem.

2. Returning from the function

Here I cannot give an exact answer to the question what VB6 expects. But here is some evidence.

2.1 In the c-function I used a direct assignment
.
.
.
float complex c = 8.0 + 30.0 * I;
return c;

and the result in VB6 was not correct. Redefining the structure to a ComplexX-Structure with the filler constant, solved this problem.

2.2 Defining my own complex type in C such as
typedef struct complex1 {
   float r;      // Real
   float i;      // Imag
}COMPLEX1;

and writing
.
.
.
COMPLEX1 c;

c.r=8.0;
c.i=30.0;
return c;

Here I clearly returned the structure and not a pointer to the structure and I got the correct result in VB6.

2.3 When I use double complex, I also return the structure and not a pointer to the structure and it works.

So I would guess, that VB6 expects the structure as a return value.


I hope, I could add some useful information to the discussion.

I learned, that you don't have access to VB6. So if you think, there should be additional tests with VB6, please inform me.

Regards

JohnF

  • Guest
Re: Complex Float gives surprising result
« Reply #20 on: August 20, 2008, 07:39:22 AM »
Hi, yes PellesC does return the structure otherwise your code that used double complex would not have worked, also if you look at the assembly you can see it returns the struct.

John

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: Complex Float gives surprising result
« Reply #21 on: August 21, 2008, 07:09:36 PM »
Ok, I'll try to be more clear.
The M$ low level standard for parameters passing says that the value returned by a function is placed in the EAX register, or in the couple EAX:EDX depending on the size. If the size is greater than the couple EAX:EDX could host the compiler automatically returns a pointer to the value in the register EAX.
This is what VB expects as return value!

Now a small structure like "float complex" occupy 32+32bits, so it fits in the couple EAX:EDX. In the case of a "double complex" the size is 64+64bits and don't fits, so a pointer is returned.

This means that when you use "double complex" VB knows that a pointer to the structure is returned and everything works well. But when you return a "float complex" VB uses contents of EAX:EDX registers. But because PellesC now diverges from M$ standard the value is not correct because EAX contains the pointer to the "float complex" structure and EDX contains rubbish.
Adding a filler that expands the size of the structure automatically instructs VB that it should expect a pointer to a structure in EAX and not a value in EAX:EDX.

See this article http://www.angelcode.com/dev/callconv/callconv.html.

Ofniw - Timovjl: I'm not expert on VB, if you know how to specify that the returned value is a pointer to structure, and not the structure itself, modify the VB code to specify this and test. If, as I suspect, is not possible to modify the expected return from VB call, you cannot use the "float complex". But if you declare a dummy structure with filler on VB side and use the standard "float complex" on C side it should work anyway with no problems while you don't try to access the filler part that doesn't effectively exist on C side.
Timovjl please could you check with debugger on VB side if register EAX, just after returning from dll call, contains effectively the address of "float complex" structure, and also check that the VB code will use EAX:EDX contents as structure value.

Please try this Timovjl modified code:
Code: [Select]
Attribute VB_Name = "Module1"
Type Complex
    r As Single                 'Real
    i As Single                 'Imaginary
    filler As Double
End Type
Type Complex_16
    r As Double                 'Real
    i As Double                 'Imaginy
End Type


Declare Function fc_add Lib "Complex_DLL.DLL" (ByRef a As Complex, ByRef c As Complex) As Complex
Declare Function dc_add Lib "Complex_DLL.DLL" (ByRef a As Complex_16, ByRef b As Complex_16) As Complex_16

Sub Main()

Dim c2 As Complex_16, c3 As Complex_16, c4 As Complex_16
Dim c5 As Complex, c6 As Complex, c7 As Complex

       ' Complex double precision Addition
        c4.r = 7
        c4.i = 30
        c2.r = 8
        c2.i = 31
        c3 = dc_add(c4, c2)
       
        ' Complex single precision, Addition
        c7.r = 7
        c7.i = 30
        c5.r = 8
        c5.i = 31
        c6 = fc_add(c7, c5)       
End Sub

To Pelle: seems that we have troubles in mixed programming diverging from M$ with structures handling, the same problem will be present also with C code produced on different compilers.
« Last Edit: August 21, 2008, 07:18:48 PM by frankie »
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

Offline TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 2091
Re: Complex Float gives surprising result
« Reply #22 on: August 22, 2008, 06:57:22 AM »
Here is that test program in VB6 binary for debugging.
May the source be with you

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: Complex Float gives surprising result
« Reply #23 on: August 22, 2008, 06:20:01 PM »
Thanks Timovjl.
Unfortunately I cannot debug VB source so I debugged the dll and accessed VB code with assembly, and as I said the problem is on VB side that adheres to the M$ calling convention while PellesC not.
In the attached picture you can see the phases of calling. While the handling of "dc_add" return is difficult to follow (but if you debug you will see that the value in EAX on return from the dll, which is the pointer to the result, is the source of data used in the "__vbaCopyBytes" function), the handling of the fc_add is very clear: VB uses EAX:EDX contents  ;D.
VB expects return values, even structures, having size up to 32+32bits in EAX:EDX, while pellesC returns always a pointer to a structure. Timovjl when you added the filler you altered the size of the structure making it larger an forcing VB to expect a pointer, that of course made the code working.  ;)

Again to Pelle: this could be a problem. At least we have to made clear that this limitation exist!  :-\

To VB experts: is it possible to declare that the returned value is a pointer??
« Last Edit: August 22, 2008, 06:26:17 PM by frankie »
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

JohnF

  • Guest
Re: Complex Float gives surprising result
« Reply #24 on: August 23, 2008, 08:34:36 AM »
It took me a while to understand, thanks Frankie.

EDIT:
Na, scrub that.

Frankie, earlier you said

Sorry John, but Pelle changed the way it return structures, he always return a pointer to the structure, even when the structure size could fit in the register pair EAX-EDX. This diverges from M$ standard.

I find that this is not true. Pelle does deal with a float complex type differently but not an 8 byte struct.

Try this code, you will see that EAX:EDX are used to return the values to the struct ComplexF.

Code: [Select]
#include <stdio.h>

typedef struct _ComplexF
{
float r;
float i;
}ComplexF;

ComplexF _stdcall fc_add(ComplexF * a1, ComplexF * a2 )
{
ComplexF c;
c.r = a1->r + a2->r;
c.i = a1->i + a2->i;
return c;
}

ComplexF fc5, fc6, fc7;

int main(void)
{
fc6.r = 7.1;
fc6.i = 7.1;
fc5.r = 7.1;
fc5.i = 7.1;
fc7 = fc_add(&fc6, &fc5);
printf("fc_add fc7 = %f+%fi\n", fc7.r, fc7.i);
return 0;
}

I made the original float values the same so you can see that EAX and EDX have the same value. Which shows they are used in the way I said.

John

« Last Edit: August 23, 2008, 05:15:03 PM by JohnF »

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: Complex Float gives surprising result
« Reply #25 on: August 23, 2008, 06:46:13 PM »
John,
you're right. I made that affirmation becouse I remembered that Pelle said this, but I cannot find the mention. At this point I don't know what think, in the case of complex types the handling of return values is not compliant with M$. It seems that for other objects the compiler is compliant.
Anyway to return a pointer to an object without any check on the object type is not correct IMHO. I refer to the first version of code (see my first guess to the problem in this thread). I mean that if you create an automatic variable (on the stack) that you want to return from a called function, and the compiler returns a pointer anyway is absolutely wrong!  ::)
If the stack is reused you will get any kind of rubbish instead of the result value!

I think that to close this issue we have to wait for the come back of Pelle. (I hope he is enjoying holydays).
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

JohnF

  • Guest
Re: Complex Float gives surprising result
« Reply #26 on: August 23, 2008, 09:08:23 PM »
Yes we have to wait for Pelle to return.

John

ofniw

  • Guest
Re: Complex Float gives surprising result
« Reply #27 on: August 24, 2008, 07:23:17 PM »
Hi everyone,

I would like to inform you about my latest findings.

The question was, is it possible to accept a pointer to a structure as a return value in VB6. That is generally not possible. Doing it anyway, in the IDE leads to an error about "wrong calling convention" or crashes.

Fortunately there is a documented bug in VB6, that at runtime, this error does not fire.

So using in C
Code: [Select]
SIMPDLLAPI float complex  WINAPI c_add(float complex * a1, float complex * a2 ){
float complex c;
c=7.0 + 30.0*I;
return c;
}   

and in VB the revised definition

Code: [Select]
Declare Function c_add_c Lib ".\simpdll\SimpDLL.DLL" Alias "c_add" (a As Complex, c As Complex) As Long

...

(the returned long can hold a 4 byte pointer )
Calling then the dll by
Dim ptr as long
.........
ptr = c_add_c(c7, c5)
c6 = PeekComplex(ptr)

with

Function PeekComplex(ByVal i As Long) As Complex
Dim c As Complex
CopyMemory ByVal VarPtr(c), ByVal i, 8
PeekComplex = c
End Function


I get in VB6 the correct result, 7+i*30, e.g. the c-function returns a pointer to a structure.

As was already posted by JohnF, this behavior is unique to float complex. It is different from the general behavior with structures.

Regards

JohnF

  • Guest
Re: Complex Float gives surprising result
« Reply #28 on: August 25, 2008, 08:25:25 AM »
No, don't do that. If you want to return a pointer do it this way

Code: [Select]
SIMPDLLAPI float complex * WINAPI fc_add(float complex * a1, float complex * a2 )
{
static float complex c;
c = *a1 + *a2;
return &c;
}   

The static keyword makes sure the var is not deleted when the function is exited, it is not created on the stack.

An alternative way is not to return anything. Pass the pointer to the receiving var in the call like so.

Code: [Select]
SIMPDLLAPI void WINAPI fc_add(float complex * a1, float complex * a2, float complex * result)
{
*result = *a1 + *a2;
}   

John
« Last Edit: August 25, 2008, 05:37:33 PM by JohnF »

ofniw

  • Guest
Re: Complex Float gives surprising result
« Reply #29 on: August 25, 2008, 07:07:34 PM »
Dear JohnF,

what I tried is to answer the question raised by Frankie in reply #21.

There he wrote:
"This means that when you use "double complex" VB knows that a pointer to the structure is returned and everything works well. But when you return a "float complex" VB uses contents of EAX:EDX registers. But because PellesC now diverges from M$ standard the value is not correct because EAX contains the pointer to the "float complex" structure and EDX contains rubbish.
Adding a filler that expands the size of the structure automatically instructs VB that it should expect a pointer to a structure in EAX and not a value in EAX:EDX"

My interpretation of this sentence was, that if you write a function for float complex

Code: [Select]
SIMPDLLAPI float complex  WINAPI c_add(float complex * a1, float complex * a2 ){
static float complex c;
c=*a1+*a2;
return c;
}   

what really happens is that the function behaves like this

Code: [Select]
SIMPDLLAPI float complex  WINAPI c_add(float complex * a1, float complex * a2 ){
static float complex c;
c=*a1+*a2;
return &c;
}   
I know this looks like a contradiction, the function is specified as returning a structure, but in reality it returns a pointer to a structure.

Frankie asked if this could be verified. That is what I tried and I would interpret my findings as a proof to the expectation of Frankie. I'm aware, that this should not be the way how to program.

Of course your proposal

Code: [Select]
SIMPDLLAPI float complex*  WINAPI c_add(float complex * a1, float complex * a2 ){
static float complex c;
c=*a1+*a2;
return &c;
}   

works correctly  with the modified VB6 program. This works even in the IDE, e.g. no error message is raised.

Code: [Select]
Declare Function c_add_c Lib ".\simpdll\SimpDLL.DLL" Alias "c_add" (a As Complex, c As Complex) As Long

...

(the returned long can hold a 4 byte pointer )
Calling then the dll by
Dim ptr as long
.........
ptr = c_add_c(c7, c5)
c6 = PeekComplex(ptr)

with

Function PeekComplex(ByVal i As Long) As Complex
Dim c As Complex
CopyMemory ByVal VarPtr(c), ByVal i, 8
PeekComplex = c
End Function

So I hope, I now understood what is going on:

If you return a float complex from a function, the function returns a pointer to the float complex. If you return another structure of the same length, e.g. what JohnF called "ComplexF", then the structure is returned.

Regards to everybody.