Pelles C forum

Pelles C => Bug reports => Topic started by: ofniw on August 10, 2008, 02:17:51 PM

Title: Complex Float gives surprising result
Post by: ofniw on August 10, 2008, 02:17:51 PM
Hi all,

I am very delighted by the addition of the complex data type with the latest version 5 of the compiler. Pelles thank you very much.

I´m using C-language to translate mathematical algorithms which I use from Visual Basic 6. ( VB6 for easy GUI programming )

In order to test the new complex types I have coded two simple routines, each adding two complex numbers :

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <oleauto.h>
#include <tgmath.h>
/*
 * Include our "interface" file and define a symbol
 * to indicate that we are *building* the DLL.
 */
#define _SIMPDLL_
#include "simpdll.h"
.
.
.
.

SIMPDLLAPI _Complex float  WINAPI c_add(float complex * a1, float complex * a2 ){
float complex c;
c=*a1+ *a2;
return c;

}

SIMPDLLAPI double complex  WINAPI dc_add(double complex* a1,  double complex* a2) {
double complex c;
c=*a1+*a2;
return c;
}

I translate these routine ( with the normal Windows overhead ) to a dll which I call from VB6.
I´m using the latest version of the 32bit version 5 compiler.


In VB 6 I declare the following complex types
Type Complex
    r As Single                 'Real
    i As Single                 'Imaginary
End Type
Type Complex_16
    r As Double                 'Real
    i As Double                 'Imaginy
End Type

With type Complex the data is stored in two consecutive single precision ( 4 bytes ) floating points.
With type Complex_16 the data is stored in two consecutive double precision ( 8 bytes ) floating points.

VB declares the two external routines as:

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

I call the routines from vb6 with
.
.
.
Dim c2 As Complex_16, c3 As Complex_16, c4 As Complex_16
Dim c5 As Complex, c6 As Complex, c7 As Complex
.
.
.
    Case Is = 8 ' Complex double precision Addition
        c4.r = 7
        c4.i = 30
        c2.r = 8
        c2.i = 31
        c3 = dc_add_c(c4, c2)
.
.
.

    Case Is = 9 ' Complex single precision, Addition
        c7.r = 7
        c7.i = 30
        c5.r = 8
        c5.i = 31
        c6 = c_add_c(c7, c5)
.
.
.
The surprising thing now is, the double precision routine gives the correct result ( via c3 ), the single precision routine delivers some nonsense in c6.

Using the debugger I get the following display after entering c_add
Name   Wert                                  Typ                             Adresse
a1        8+31i                                 float_Complex *8         0012F574
a2     1.74135e-39+5.89257e-39i       float_Complex *8         0012F61C
c       1.7413e-39+5.89477e-39i        float_Complex *8         0012F318

I searched the memory and found a1 at 0012F520 and a2 at 0012F574, not a1.

So, now I'm not sure,if I´m supposed to use the compiler the way I use it. However the double precision function works. So, may be I'm doing something wrong in calling the single precision routine, or the way the arguments are transfered is different for the single precision case, or there is a bug in the compiler.

Any idea ?

Regards
Title: Re: Complex Float gives surprising result
Post by: JohnF on August 10, 2008, 04:09:15 PM
I don't know much about VB but shouldn't you be declaring the vars as ByRef

Declare Function c_add_c Lib ".\simpdll\SimpDLL.DLL" Alias "c_add" (ByRef a As Complex, ByRef c As Complex) As Complex
Declare Function dc_add_c Lib ".\simpdll\SimpDLL.DLL" Alias "dc_add" (ByRef a As Complex_16, ByRef b As Complex_16) As Complex_16

Here is a sample listing that shows your functions work the same as an ordinary complex add, so the problem I think must be in you VB code or VB calls.

Code: [Select]
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <complex.h>


float complex WINAPI fc_add(float complex * a1, float complex * a2)
{
float complex fc;
fc = *a1 + *a2;
return fc;
}

double complex WINAPI dc_add(double complex * a1, double complex * a2)
{
double complex dc;
dc = *a1 + *a2;
return dc;
}

int main(void)
{
double complex dc2, dc3, dc4;
float complex fc5, fc6, fc7;

dc4 = 7+I*30;
dc2 = 8+I*31;
dc3 = dc_add(&dc4, &dc2);

// printf the result of dc_add()
printf("dc3 = %f, %f\n", creal(dc3), cimag(dc3));

// check dc_add
dc3 = dc4 + dc2;
printf("dc3 = %f, %f\n", creal(dc3), cimag(dc3));

fc7 = 7+I*30;
fc5 = 8+I*31;
fc6 = fc_add(&fc7, &fc5);

// printf the result of fc_add()
  printf("fc6 = %f, %f\n", crealf(fc6), cimagf(fc6));

// check fc_add
fc6 = fc7 + fc5;
  printf("fc6 = %f, %f\n", crealf(fc6), cimagf(fc6));

return 0;
}

John
Title: Re: Complex Float gives surprising result
Post by: ofniw on August 11, 2008, 09:56:17 PM
JohnF, thank you very much for your immediate reply. It is correct, that the call from VB6 has to use the byref convention. However, this is the default. In order not to miss any chance, I added the byref keyword and nothing changed, the single precision add didn't work, the double precision worked.

In the meantime, I also believe, that there is something wrong in passing the addresses of the variables to the subroutine. The question is on which side, VB6 or PellesC.

So I did another test. I defined an own complex type called COMPLEX1:

typedef struct complex1 {
   float r;      // Real
   float i;      // Imag
}COMPLEX1;

Then I replaced the c_add function by:

SIMPDLLAPI COMPLEX1  WINAPI c_add(COMPLEX1 * a1,COMPLEX1 * a2 ){
COMPLEX1 c;
c.r=a1->r+ a2->r;
c.i=a1->i+ a2->i;
return c;

}

And this worked. So I believe, the problem is not with VB6, also because the double precision version works correctly.

Regards
Title: Re: Complex Float gives surprising result
Post by: JohnF on August 11, 2008, 11:11:11 PM
Well, I did another test with a DLL, all ok. So I still reckon the problem is not clear. If it works with a PellesC DLL it should work from VB provided VB is doing the same thing.

Try observing the addresses of the variables from both your VB app and the DLL and see if there is a problem there. An easy way is to use a MessageBox.

Is it possible that the VB float struct has padding and the double struct does not have padding?

If you did your COMPLEX1 test in C that is not much of a test as my code had already done that.

Try to get the size of the VB complex float struct.

John
 

Title: Re: Complex Float gives surprising result
Post by: ofniw on August 12, 2008, 09:30:32 PM
Thank you for your response.

I followed your advice and did the following:
1. Got the location of the vb6 structures
2. Using the debugger and my "own complex type" examined the pointers in the c function
3. Using the built in complex type examined the pointers in the c function

These are the results:
1.
Thats the code snippet from VB6

        c7.r = 7
        c7.i = 30
        c5.r = 8
        c5.i = 31
        c6 = c_add_c(c7, c5)

I have placed a breakpoint before the function call. At that breakpoint I get for the addresses of the complex structures:

?hex(varptr(c7))
13F520
?hex(varptr(c7.r))
13F520
?hex(varptr(c7.i))
13F524
?hex(varptr(c5))
13F574
?hex(varptr(c5.r))
13F574
?hex(varptr(c5.i))
13F578
This is what I had already written, that the values are stored in consecutive locations.
#######################
Now to the c-side
2.
With the construct

SIMPDLLAPI COMPLEX1  WINAPI c_add(COMPLEX1 * a1,COMPLEX1 * a2 ){
COMPLEX1 c;
c.r=a1->r+ a2->r;
c.i=a1->i+ a2->i;
return c;

In the debugger I get the information

Name                     Value                                      Type                                  Address 
a1                           {...}                                     struct*                               0013F520
       r                      7                                          struct*                               0013F520
       i                       30                                        struct*                               0013F524
a2                           {...}                                     struct*                               0013F574
       r                      8                                          struct*                               0013F574
       i                       31                                        struct*                               0013F578

which is exactly the same location that I got in VB6.
3.
Now, without changing anything in VB6 but using the construct

SIMPDLLAPI float complex  WINAPI c_add(float complex * a1, float complex * a2 ){
float complex c;
c=*a1+ *a2;
return c;

}
From the debugger I get

Name   Value                                 Type                           Address
a1        8+31i                                 float_Complex *8         0012F574
a2     1.74135e-39+5.89257e-39i       float_Complex *8         0012F61C
c       1.7413e-39+5.89477e-39i        float_Complex *8         0012F318

So obviously, *a1 and *a2 point to the wrong location. They should point at

*a1    0012F520
*a2    0012F574


I hope this examination helps.

Regards

Title: Re: Complex Float gives surprising result
Post by: JohnF on August 12, 2008, 10:40:13 PM
Using a DLL I get the correct addresses so I'm stumped.

Why would VB cause a difference between your COMPLEX1 struct and a Complex type? I can't guess at that.

Of course you are suggesting that the two different functions in PellesC are causing the problem.

Code: [Select]
SIMPDLLAPI float complex  WINAPI c_add(float complex * a1, float complex * a2)

SIMPDLLAPI COMPLEX1  WINAPI c_add(COMPLEX1 * a1,COMPLEX1 * a2 )

The fact that it works with my DLL suggests that there is no problem.

Have you tried making a test using a C listing and your DLL and see what happens then ?

Here is my listing again. Put the lib of your dll and the dll in the same folder as the C app and run the code below. Change the function calls to suit the names you have used in your dll and of course the name of your dll.

Code: [Select]
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <complex.h>

#pragma lib "yourdll.lib" // your name here


float complex WINAPI fc_add(float complex * a1, float complex * a2);
double complex WINAPI dc_add(double complex * a1, double complex * a2);

int main(void)
{
double complex dc2, dc3, dc4;
float complex fc5, fc6, fc7;

dc4 = 7+I*30;
dc2 = 8+I*31;
dc3 = dc_add(&dc4, &dc2);

// printf the result of dc_add()
printf("dc3 = %f+%fi\n", creal(dc3), cimag(dc3));

// check dc_add
dc3 = dc4 + dc2;
printf("dc3 = %f+%fi\n", creal(dc3), cimag(dc3));

fc7 = 7+I*30;
fc5 = 8+I*31;
fc6 = fc_add(&fc7, &fc5);

// printf the result of fc_add()
  printf("fc6 = %f+%fi\n", crealf(fc6), cimagf(fc6));

// check fc_add
fc6 = fc7 + fc5;
  printf("fc6 = %f+%fi\n", crealf(fc6), cimagf(fc6));

return 0;
}

John
Title: Re: Complex Float gives surprising result
Post by: TimoVJL on August 13, 2008, 10:21:49 AM
Test this:
Code: [Select]
Attribute VB_Name = "Module1"
Type Complex
    r As Single                 'Real
    i As Single                 'Imaginary
End Type
Type Complex_16
    r As Double                 'Real
    i As Double                 'Imaginy
End Type
Type ComplexX
    r As Single                 'Real
    i As Single                 'Imaginary
    filler As Double
End Type


'Declare Function fc_add Lib "Complex_DLL.DLL" (ByRef a As Complex, ByRef c As Complex) As Complex
Declare Function fc_add Lib "Complex_DLL.DLL" (ByRef a As Complex, ByRef c As Complex) As ComplexX
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
Dim cx As ComplexX

       ' 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)
        cx = fc_add(c7, c5)
       
End Sub
Title: Re: Complex Float gives surprising result
Post by: JohnF on August 13, 2008, 10:56:50 AM
Timovjl, what have you found? I can't run VB stuff.

John
Title: Re: Complex Float gives surprising result
Post by: TimoVJL on August 13, 2008, 01:59:17 PM
Some kind of stack problem.
With filler it can be avoided.

Code: [Select]
CPU Disasm
Address   Hex dump          Command                                  Comments
10001000    55              PUSH EBP
10001001    89E5            MOV EBP,ESP
10001003    83EC 08         SUB ESP,8
10001006    8B45 0C         MOV EAX,DWORD PTR SS:[EBP+0C]
10001009    D940 04         FLD DWORD PTR DS:[EAX+4]
1000100C    D900            FLD DWORD PTR DS:[EAX]
1000100E    8B45 10         MOV EAX,DWORD PTR SS:[EBP+10]
10001011    D800            FADD DWORD PTR DS:[EAX]                  ; FLOAT 8.000000000000000
10001013    D9C9            FXCH ST(1)
10001015    D840 04         FADD DWORD PTR DS:[EAX+4]
10001018    D9C9            FXCH ST(1)
1000101A    D95D F8         FSTP DWORD PTR SS:[EBP-8]
1000101D    D95D FC         FSTP DWORD PTR SS:[EBP-4]
10001020    8B45 08         MOV EAX,DWORD PTR SS:[EBP+8]
10001023    D945 FC         FLD DWORD PTR SS:[EBP-4]
10001026    D945 F8         FLD DWORD PTR SS:[EBP-8]
10001029    D918            FSTP DWORD PTR DS:[EAX]
1000102B    D958 04         FSTP DWORD PTR DS:[EAX+4]
1000102E    89EC            MOV ESP,EBP
10001030    5D              POP EBP
10001031    C2 0C00         RETN 0C

10001034    55              PUSH EBP
10001035    89E5            MOV EBP,ESP
10001037    83EC 10         SUB ESP,10
1000103A    8B45 0C         MOV EAX,DWORD PTR SS:[EBP+0C]
1000103D    DD40 08         FLD QWORD PTR DS:[EAX+8]
10001040    DD00            FLD QWORD PTR DS:[EAX]
10001042    8B45 10         MOV EAX,DWORD PTR SS:[EBP+10]
10001045    DC00            FADD QWORD PTR DS:[EAX]
10001047    D9C9            FXCH ST(1)
10001049    DC40 08         FADD QWORD PTR DS:[EAX+8]
1000104C    D9C9            FXCH ST(1)
1000104E    DD5D F0         FSTP QWORD PTR SS:[EBP-10]
10001051    DD5D F8         FSTP QWORD PTR SS:[EBP-8]
10001054    8B45 08         MOV EAX,DWORD PTR SS:[EBP+8]
10001057    DD45 F8         FLD QWORD PTR SS:[EBP-8]
1000105A    DD45 F0         FLD QWORD PTR SS:[EBP-10]
1000105D    DD18            FSTP QWORD PTR DS:[EAX]
1000105F    DD58 08         FSTP QWORD PTR DS:[EAX+8]
10001062    89EC            MOV ESP,EBP
10001064    5D              POP EBP
10001065    C2 0C00         RETN 0C
Title: Re: Complex Float gives surprising result
Post by: JohnF on August 13, 2008, 04:52:48 PM
Ok, thanks.

John
Title: Re: Complex Float gives surprising result
Post by: ofniw on August 13, 2008, 08:15:47 PM
Good evening JohnF and timovjl

Hello JohnF,
I first tried your proposed small C program and it gave the correct result with the same dll I used with the VB6 program.

Hello timovjl,
then I tried your redefinition complexX of the type complex ( adding filler ). Then I got the correct result with VB6.

Thanks, however, I do not understand what`s going on.

Regards
Title: Re: Complex Float gives surprising result
Post by: JohnF on August 14, 2008, 09:29:48 AM
Yes it's strange. I did a test with VC6 and the Complex struct with two floats crashes whereas the ComplexX struct with an extra double does not.

Haven't seen Pelle for a while, hope he sees this thread.

EDIT: The above also crashes in a PellesC listing.

John
Title: Re: Complex Float gives surprising result
Post by: frankie on August 14, 2008, 05:05:08 PM
Dear all,
I had not enaugh time to check everything, and I, as John, don't have VB available. But as far as I can see you are returning automatic variables pinters from your programs  ::), this kind of variables as you well know are destroyed when the subroutine exits. This explains the crash, and maybe the wrong addressing (due to processo memory mappings).
I'm not sure this will solve, but I suggest to change the dll code to:
Code: [Select]
complex float   __c_float;
SIMPDLLAPI _Complex float  WINAPI *c_add(float complex * a1, float complex * a2 ){
__c_float=*a1+ *a2;
return &__c_float;
}

complex double   __c_double;
SIMPDLLAPI double complex  WINAPI *dc_add(double complex* a1,  double complex* a2) {
__c_double=*a1+*a2;
return &__c_double;
}
Please test, I'm curious....
Title: Re: Complex Float gives surprising result
Post by: ofniw on August 14, 2008, 07:12:02 PM
Hello Frankie,
I have tested.

I have used 4 setups

Setup 1 and Setup 2 use the program as you suggested

Setup 1:
--------
Type Complex
    r As Single                 'Real
    i As Single                 'Imaginary
End Type
Type Complex_16
    r As Double                 'Real
    i As Double                 'Imaginary
End Type

Result:
Simple precision: Wrong result
Double precision: Crash

Setup 2:
--------
Type ComplexX
    r As Single                 'Real
    i As Single                 'Imaginary
    filler As Double
End Type
Type Complex_16
    r As Double                 'Real
    i As Double                 'Imaginary
End Type

Result:
Simple precision: Crash
Double precision: Crash

Setup 3 and Setup 4 use the following program

complex float   __c_float;
SIMPDLLAPI _Complex float  WINAPI c_add(float complex * a1, float complex * a2 ){
__c_float=*a1+ *a2;
return __c_float;
}

complex double   __c_double;
SIMPDLLAPI double complex  WINAPI dc_add(double complex* a1,  double complex* a2) {
__c_double=*a1+*a2;
return __c_double;
}

Setup 3:
--------
Type Complex
    r As Single                 'Real
    i As Single                 'Imaginary
End Type
Type Complex_16
    r As Double                 'Real
    i As Double                 'Imaginary
End Type

Result:
Double precision: Correct result.
Single precision: Wrong result


Setup 4
--------
With the definition
'    filler As Double
Type ComplexX
    r As Single                 'Real
    i As Single                 'Imaginary
    filler As Double
End Type
Type Complex_16
    r As Double                 'Real
    i As Double                 'Imaginary
End Type

Result:
Double precision: Correct result.
Single precision: Correct result


So sorry, your suggestion did not work.

Regards
Title: Re: Complex Float gives surprising result
Post by: JohnF on August 14, 2008, 08:08:07 PM
Dear all,
I had not enaugh time to check everything, and I, as John, don't have VB available. But as far as I can see you are returning automatic variables pinters from your programs  ::), this kind of variables as you well know are destroyed when the subroutine exits. This explains the crash, and maybe the wrong addressing (due to processo memory mappings).

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.

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

There _is_ something wrong here.

John
Title: Re: Complex Float gives surprising result
Post by: JohnF 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
Title: Re: Complex Float gives surprising result
Post by: ofniw 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.

Title: Re: Complex Float gives surprising result
Post by: frankie 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.
Title: Re: Complex Float gives surprising result
Post by: JohnF 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
Title: Re: Complex Float gives surprising result
Post by: ofniw 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
Title: Re: Complex Float gives surprising result
Post by: JohnF 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
Title: Re: Complex Float gives surprising result
Post by: frankie 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 (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.
Title: Re: Complex Float gives surprising result
Post by: TimoVJL on August 22, 2008, 06:57:22 AM
Here is that test program in VB6 binary for debugging.
Title: Re: Complex Float gives surprising result
Post by: frankie 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??
Title: Re: Complex Float gives surprising result
Post by: JohnF 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

Title: Re: Complex Float gives surprising result
Post by: frankie 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).
Title: Re: Complex Float gives surprising result
Post by: JohnF on August 23, 2008, 09:08:23 PM
Yes we have to wait for Pelle to return.

John
Title: Re: Complex Float gives surprising result
Post by: ofniw 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
Title: Re: Complex Float gives surprising result
Post by: JohnF 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
Title: Re: Complex Float gives surprising result
Post by: ofniw 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.
Title: Re: Complex Float gives surprising result
Post by: JohnF on August 25, 2008, 08:04:45 PM
Ok, if you use code that returns a pointer make sure you use the code I showed you. :)

Btw, I think using the void routine, the one that doesn't return anything is more efficient.

John
Title: Re: Complex Float gives surprising result
Post by: Pelle on September 23, 2008, 05:27:50 PM
The complex data types (float complex, double complex, long double complex) have a memory layout where the imaginary part immediately follows the real part, which (IIRC) is a requirement. It may look like a structure, but I don't consider it as such. It would perhaps be convenient, for cross-language development, if float complex were passed like a struct, but this would cause me an endless stream of problems (much more than struct's). 

I started in the computer business 1981, and have managed to stay away from complex numbers so far. I suspect I'm not the only one. In other words: I personally think the addition of complex numbers in C99 was a mistake, but since it's part of the current standard I have (finally) added it. I will not, however, do more than absolutely necessary to make it work.
Title: Re: Complex Float gives surprising result
Post by: JohnF on September 23, 2008, 06:41:38 PM
Welcome back!

John
Title: Re: Complex Float gives surprising result
Post by: Pelle on September 23, 2008, 08:59:33 PM
Thank you!
Title: Re: Complex Float gives surprising result
Post by: frankie on September 24, 2008, 11:22:05 AM
Nice to hear you again Pelle!  :D

I agree with you about complex numbers (life is already complex enough to me!  ;) ).
Anyway please consider only the issue related to automatic variables returned by functions, this could be a bomb also when cross-language is not involved (especially if some function modify it when the stack has been reused!).
Title: Re: Complex Float gives surprising result
Post by: Pelle on September 24, 2008, 03:43:56 PM
Anyway please consider only the issue related to automatic variables returned by functions, this could be a bomb also when cross-language is not involved (especially if some function modify it when the stack has been reused!).
I have just glanced through this thread, so I may be missing something obvious...

Structures with a size different from 1/2/4/8 bytes, and all complex numbers, are returned using a "hidden" pointer to a location provided by the caller (for example, a function with a 'void' parameter list, and return type of 'float _Complex', will actually receive one pointer argument). The callee will copy the relevant bytes to the provided location, using the hidden pointer. When compiling a function that receives such a struct or complex number, the compiler will sometimes (when needed) create temporary stack objects for this. AFAIK, all of this is working as it should...

Is this what you are talking about? Is there a problem here?
Title: Re: Complex Float gives surprising result
Post by: frankie on September 24, 2008, 04:16:29 PM
Unfortunately I don't have time to check extensively. As far I can see you always copy contents of the returned structure, don't pass the pointer so there should be no problem. I don't see, in my trials the creation of the "hidden" pointer that you mention, but this should be due to my limited checks. I don't know if does exist a construct that insert an operation between the function call and the result assignement that could create problems.