Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #158393 > unrolled thread

CMPLX vs a+bi values

Started bybeej@beej.us (Beej)
First post2021-01-16 06:27 +0000
Last post2021-01-27 07:30 -0800
Articles 14 — 4 participants

Back to article view | Back to comp.lang.c


Contents

  CMPLX vs a+bi values beej@beej.us (Beej) - 2021-01-16 06:27 +0000
    Re: CMPLX vs a+bi values James Kuyper <jameskuyper@alumni.caltech.edu> - 2021-01-16 11:18 -0500
      Re: CMPLX vs a+bi values "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2021-01-20 13:52 -0800
        Re: CMPLX vs a+bi values "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2021-01-20 14:01 -0800
    Re: CMPLX vs a+bi values Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-01-20 07:20 -0800
      Re: CMPLX vs a+bi values beej@beej.us (Beej) - 2021-01-26 02:13 +0000
        Re: CMPLX vs a+bi values Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-01-27 06:19 -0800
    Re: CMPLX vs a+bi values "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2021-01-20 13:39 -0800
      Re: CMPLX vs a+bi values "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2021-01-20 13:40 -0800
      Re: CMPLX vs a+bi values James Kuyper <jameskuyper@alumni.caltech.edu> - 2021-01-20 17:40 -0500
        Re: CMPLX vs a+bi values "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2021-01-20 14:57 -0800
          Re: CMPLX vs a+bi values James Kuyper <jameskuyper@alumni.caltech.edu> - 2021-01-20 19:21 -0500
            Re: CMPLX vs a+bi values "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2021-01-20 19:44 -0800
    Re: CMPLX vs a+bi values Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-01-27 07:30 -0800

#158393 — CMPLX vs a+bi values

Frombeej@beej.us (Beej)
Date2021-01-16 06:27 +0000
SubjectCMPLX vs a+bi values
Message-ID<rtu0vv$h9q$1@dont-email.me>
Time for another esoteric question!

----------

#include <complex.h>

double complex a = CMPLX(1, 2);
double complex b = 1 + 2*I;

----------

They compare equal, of course.

Are there any differences between the two values, i.e. whether or not
CMPLX() is used?

Right before posting, another search took me here:

https://en.cppreference.com/w/c/numeric/complex/Imaginary_I

Where they write of _Imaginary_I:

> This macro allows for the precise way to assemble a complex number
> from its real and imaginary components, e.g. with (double
> complex)((double)x + _Imaginary_I * (double)y). This pattern was
> standardized in C11 as the macro CMPLX. Note that if _Complex_I is
> used instead, this expression is allowed to convert negative zero to
> positive zero in the imaginary position.

And in C11 7.3.9.3p4:

> NOTE These macros act as if the implementation supported imaginary
> types and the definitions were:
>
>  #define CMPLX(x, y) ((double complex)((double)(x) + \
>                                      _Imaginary_I * (double)(y)))

calling out _Imaginary_I explicitly.

So maybe on an implementation where I is _Complex_I, a non-CMPLX complex
value might be allowed to convert a negative zero to positive--in
contrast to a CMPLX()-created value...?

More implications in C11 7.3.3 (Branch Cuts).

Am I on the right track?

-Beej

[toc] | [next] | [standalone]


#158396

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2021-01-16 11:18 -0500
Message-ID<rtv3l8$9of$1@dont-email.me>
In reply to#158393
On 1/16/21 1:27 AM, Beej wrote:
> Time for another esoteric question!
> 
> ----------
> 
> #include <complex.h>
> 
> double complex a = CMPLX(1, 2);
> double complex b = 1 + 2*I;
> 
> ----------
> 
> They compare equal, of course.
> 
> Are there any differences between the two values, i.e. whether or not
> CMPLX() is used?

In principle, unfortunately, yes, but not because of your use of CMPLX().

"The accuracy of the floating-point operations (+, -, *, /) and of the
library functions in <math.h> and <complex.h> that return floating-point
results is implementation-defined, as is the accuracy of the conversion
between floating-point internal representations and string
representations performed by the library functions in <stdio.h>,
<stdlib.h>, and <wchar.h>. The implementation may state that the
accuracy is unknown." (5.2.4.2.2p6).

In principle, this means that an implementation would be within its
rights to implement subtraction so inaccurately that
	LDBL_EPSILON - LDBL_MAX > LDBL_MAX - LDBL_EPSILON
evaluate to true.

If __STDC_IEC_599__ is pre#defined by the implementation, then the
optional requirements specified in Annex F are met, which means that
many requirements of IEC 60559 (== ANSI/ISEE 754) apply, including
pretty much the strongest accuracy requirements that it is feasible to
mandate for floating point math. That's good enough to prohibit my
example from failing, but it's still lenient enough to allow identical
floating point expressions with no side effects to have results that are
not equal.

[toc] | [prev] | [next] | [standalone]


#158512

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2021-01-20 13:52 -0800
Message-ID<rua8mo$1mmi$1@gioia.aioe.org>
In reply to#158396
On 1/16/2021 8:18 AM, James Kuyper wrote:
> On 1/16/21 1:27 AM, Beej wrote:
>> Time for another esoteric question!
>>
>> ----------
>>
>> #include <complex.h>
>>
>> double complex a = CMPLX(1, 2);
>> double complex b = 1 + 2*I;
>>
>> ----------
>>
>> They compare equal, of course.
>>
>> Are there any differences between the two values, i.e. whether or not
>> CMPLX() is used?
> 
> In principle, unfortunately, yes, but not because of your use of CMPLX().
> 
> "The accuracy of the floating-point operations (+, -, *, /) and of the
> library functions in <math.h> and <complex.h> that return floating-point
> results is implementation-defined, as is the accuracy of the conversion
> between floating-point internal representations and string
> representations performed by the library functions in <stdio.h>,
> <stdlib.h>, and <wchar.h>. The implementation may state that the
> accuracy is unknown." (5.2.4.2.2p6).
> 
> In principle, this means that an implementation would be within its
> rights to implement subtraction so inaccurately that
> 	LDBL_EPSILON - LDBL_MAX > LDBL_MAX - LDBL_EPSILON
> evaluate to true.
> 
> If __STDC_IEC_599__ is pre#defined by the implementation, then the
> optional requirements specified in Annex F are met, which means that
> many requirements of IEC 60559 (== ANSI/ISEE 754) apply, including
> pretty much the strongest accuracy requirements that it is feasible to
> mandate for floating point math. That's good enough to prohibit my
> example from failing, but it's still lenient enough to allow identical
> floating point expressions with no side effects to have results that are
> not equal.
> 

Ohhh yeah. Check this out... A way to store real data in the roots of 
complex numbers. It suffers from accumulating floating point errors in 
the form of data loss when one tries to store "too much" data. Then 
again, it depends on the data itself. Sometimes, my experiment can 
actually compress things, strange:

https://groups.google.com/g/comp.lang.c++/c/bB1wA4wvoFc/m/OTccTiXLAgAJ

Pure C99:

https://github.com/ChrisMThomasson/fractal_cipher/blob/master/RIFC/cpp/ct_rifc_sample.cpp

[toc] | [prev] | [next] | [standalone]


#158513

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2021-01-20 14:01 -0800
Message-ID<rua97o$1t87$1@gioia.aioe.org>
In reply to#158512
On 1/20/2021 1:52 PM, Chris M. Thomasson wrote:
> On 1/16/2021 8:18 AM, James Kuyper wrote:
[...]
> Pure C99:

Damn it! The following is in C++. Sorry. Facepalm.

> 
> https://github.com/ChrisMThomasson/fractal_cipher/blob/master/RIFC/cpp/ct_rifc_sample.cpp 
> 

[toc] | [prev] | [next] | [standalone]


#158482

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2021-01-20 07:20 -0800
Message-ID<86h7nbocsh.fsf@linuxsc.com>
In reply to#158393
beej@beej.us (Beej) writes:

> Time for another esoteric question!
>
> ----------
>
> #include <complex.h>
>
> double complex a = CMPLX(1, 2);
> double complex b = 1 + 2*I;
>
> ----------
>
> They compare equal, of course.
>
> Are there any differences between the two values, i.e. whether or
> not CMPLX() is used?  [...]

I have some comments on this question.  First though I am
curious to know if you read my response to your question
about floodfill.

[toc] | [prev] | [next] | [standalone]


#158620

Frombeej@beej.us (Beej)
Date2021-01-26 02:13 +0000
Message-ID<runtsh$e72$1@dont-email.me>
In reply to#158482
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
> I have some comments on this question.  First though I am
> curious to know if you read my response to your question
> about floodfill.

I did; I just didn't have anything additional to add.

-Beej

[toc] | [prev] | [next] | [standalone]


#158645

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2021-01-27 06:19 -0800
Message-ID<86sg6mlaxx.fsf@linuxsc.com>
In reply to#158620
beej@beej.us (Beej) writes:

> Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
>
>> I have some comments on this question.  First though I am
>> curious to know if you read my response to your question
>> about floodfill.
>
> I did;  I just didn't have anything additional to add.

I wasn't sure what your reactions were or even if you had
read it at all.  I went to some amount of effort to put a
response together, so normally I would expect to see at
least an acknowledgement, and a bit more if any of the
response was new to you.  Just to say that directly.

[toc] | [prev] | [next] | [standalone]


#158510

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2021-01-20 13:39 -0800
Message-ID<rua7ue$1c1u$1@gioia.aioe.org>
In reply to#158393
On 1/15/2021 10:27 PM, Beej wrote:
> Time for another esoteric question!
> 
> ----------
> 
> #include <complex.h>
> 
> double complex a = CMPLX(1, 2);
> double complex b = 1 + 2*I;
> 
> ----------
> 
> They compare equal, of course.
> 
> Are there any differences between the two values, i.e. whether or not
> CMPLX() is used?
[...]

Well, CMPLX seems to perform some casts:

https://en.cppreference.com/w/c/numeric/complex/CMPLX

I am not sure if the raw form 1 + 2*I does these under the hood.

[toc] | [prev] | [next] | [standalone]


#158511

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2021-01-20 13:40 -0800
Message-ID<rua818$1c1u$2@gioia.aioe.org>
In reply to#158510
On 1/20/2021 1:39 PM, Chris M. Thomasson wrote:
> On 1/15/2021 10:27 PM, Beej wrote:
>> Time for another esoteric question!
>>
>> ----------
>>
>> #include <complex.h>
>>
>> double complex a = CMPLX(1, 2);
>> double complex b = 1 + 2*I;
>>
>> ----------
>>
>> They compare equal, of course.
>>
>> Are there any differences between the two values, i.e. whether or not
>> CMPLX() is used?
> [...]
> 
> Well, CMPLX seems to perform some casts:
> 
> https://en.cppreference.com/w/c/numeric/complex/CMPLX
> 
> I am not sure if the raw form 1 + 2*I does these under the hood.
> 

CMPLX might suppress some warnings.

[toc] | [prev] | [next] | [standalone]


#158514

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2021-01-20 17:40 -0500
Message-ID<ruabhh$at2$1@dont-email.me>
In reply to#158510
On 1/20/21 4:39 PM, Chris M. Thomasson wrote:
> On 1/15/2021 10:27 PM, Beej wrote:
>> Time for another esoteric question!
>>
>> ----------
>>
>> #include <complex.h>
>>
>> double complex a = CMPLX(1, 2);
>> double complex b = 1 + 2*I;
>>
>> ----------
>>
>> They compare equal, of course.
>>
>> Are there any differences between the two values, i.e. whether or not
>> CMPLX() is used?
> [...]
> 
> Well, CMPLX seems to perform some casts:
> 
> https://en.cppreference.com/w/c/numeric/complex/CMPLX
> 
> I am not sure if the raw form 1 + 2*I does these under the hood.

CMPLX casts both of it's arguments to double. It's supposed to behave as
though it uses _Imaginary_I, even on implementations that don't support
_Imaginary types. That shouldn't matter for the particular values being
used here. The multiplication results in a value of _Imaginary double
type. The addition gives a result of _Complex double type.

Those exact same conversions occur implicitly in 1 + 2*I, with one minor
complication. I can expand to either _Imaginary_I or _Complex_I. If it
is _Complex_I, then 2*I has _Complex double type rather than _Imaginary
double, however, it has the same value regardless of which type it has.
When 1 is added to it, 1 is converted to double, and the result is
_Complex double.

It shouldn't make any difference which of the two you use.

[toc] | [prev] | [next] | [standalone]


#158515

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2021-01-20 14:57 -0800
Message-ID<ruachi$18da$1@gioia.aioe.org>
In reply to#158514
On 1/20/2021 2:40 PM, James Kuyper wrote:
> On 1/20/21 4:39 PM, Chris M. Thomasson wrote:
>> On 1/15/2021 10:27 PM, Beej wrote:
>>> Time for another esoteric question!
>>>
>>> ----------
>>>
>>> #include <complex.h>
>>>
>>> double complex a = CMPLX(1, 2);
>>> double complex b = 1 + 2*I;
>>>
>>> ----------
>>>
>>> They compare equal, of course.
>>>
>>> Are there any differences between the two values, i.e. whether or not
>>> CMPLX() is used?
>> [...]
>>
>> Well, CMPLX seems to perform some casts:
>>
>> https://en.cppreference.com/w/c/numeric/complex/CMPLX
>>
>> I am not sure if the raw form 1 + 2*I does these under the hood.
> 
> CMPLX casts both of it's arguments to double. It's supposed to behave as
> though it uses _Imaginary_I, even on implementations that don't support
> _Imaginary types.

Did not know that. To create an I... well:

double complex I = CMPLX(0, 1);

should do.

> That shouldn't matter for the particular values being
> used here. The multiplication results in a value of _Imaginary double
> type. The addition gives a result of _Complex double type.
> 
> Those exact same conversions occur implicitly in 1 + 2*I, with one minor
> complication. I can expand to either _Imaginary_I or _Complex_I. If it
> is _Complex_I, then 2*I has _Complex double type rather than _Imaginary
> double, however, it has the same value regardless of which type it has.
> When 1 is added to it, 1 is converted to double, and the result is
> _Complex double.
> 
> It shouldn't make any difference which of the two you use.
> 

Agreed.

[toc] | [prev] | [next] | [standalone]


#158516

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2021-01-20 19:21 -0500
Message-ID<ruahe1$ghg$1@dont-email.me>
In reply to#158515
On 1/20/21 5:57 PM, Chris M. Thomasson wrote:
> On 1/20/2021 2:40 PM, James Kuyper wrote:
...
>> CMPLX casts both of it's arguments to double. It's supposed to behave as
>> though it uses _Imaginary_I, even on implementations that don't support
>> _Imaginary types.
> 
> Did not know that. To create an I... well:
> 
> double complex I = CMPLX(0, 1);

That's equivalent to _Complex_I, and _Complex_I is one of the two
permissible permitted expansions for I.

[toc] | [prev] | [next] | [standalone]


#158517

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2021-01-20 19:44 -0800
Message-ID<ruatal$pq5$1@gioia.aioe.org>
In reply to#158516
On 1/20/2021 4:21 PM, James Kuyper wrote:
> On 1/20/21 5:57 PM, Chris M. Thomasson wrote:
>> On 1/20/2021 2:40 PM, James Kuyper wrote:
> ...
>>> CMPLX casts both of it's arguments to double. It's supposed to behave as
>>> though it uses _Imaginary_I, even on implementations that don't support
>>> _Imaginary types.
>>
>> Did not know that. To create an I... well:
>>
>> double complex I = CMPLX(0, 1);
> 
> That's equivalent to _Complex_I, and _Complex_I is one of the two
> permissible permitted expansions for I.
> 

Thanks. I am not totally familiar with C99 complex.

https://en.cppreference.com/w/c/numeric/complex/Complex_I

The following quote is interesting to me:

"Unlike _Imaginary_I and CMPLX, use of this macro to construct a complex 
number may lose the sign of zero on the imaginary component."

[toc] | [prev] | [next] | [standalone]


#158648

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2021-01-27 07:30 -0800
Message-ID<86o8hal7nn.fsf@linuxsc.com>
In reply to#158393
beej@beej.us (Beej) writes:

> Time for another esoteric question!
>
> ----------
>
> #include <complex.h>
>
> double complex a = CMPLX(1, 2);
> double complex b = 1 + 2*I;
>
> ----------
>
> They compare equal, of course.  [..discussion of possible
> differences..]

Normally I would prefer to avoid either form.  If what you care
about is complex values in ordinary code (ie, in statements or
initializers for non-static locals), you can use something
like this:

    typedef double _Complex                 Z;

    typedef union { double xy[2]; Z z; }    Union_xy_Z;

    #define COMPLEX(x,y)  (   (Union_xy_Z){ .xy[0]=(x), .xy[1]=(y) }.z   )

    Z
    minus_one_example_a(){
        Z i = COMPLEX( 0, 1 );
        return  i*i;
    }

This COMPLEX macro does not need any header, and works in both
C99 and C11 (provided of course _Complex is supported).  It does
have a drawback, in that it cannot be used in initializers at the
top level (what the C standard calls "external definitions").
However, we can define a macro that gives a value of a pointer to
a complex:

    #define A_COMPLEX(x,y)  (   (Z*)  &COMPLEX( x, y )   )

    static const Z *const pI = A_COMPLEX( 0, 1 );

    Z
    minus_one_example_b(){
        return  *pI * *pI;
    }

Note that the variable 'pI' need not be declared 'const' (for
either or both of the two 'const' modifiers in the declaration).

The A_COMPLEX macro works for external definitions, but because
of how compound literals are defined A_COMPLEX does not work for
'static' declarations inside a function.  So if we really need a
function-local static, an explicit intermediate object can be
used to do that:

    Z
    minus_one_example_c(){
     static Union_xy_Z xy_I = {{ 0, 1 }};
        Z *const pI = & xy_I.z;
        return  *pI * *pI;
    }

The expression '*pI' can be used to update as well as read the
function-local static object xy_I.z.

All the above is required by the C standard to work in C.
Furthermore, because the two components of a complex are stored
directly, rather than being combined using multiplication or
addition, losing the sign of a (floating) negative zero shouldn't
ever happen.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c


csiph-web