May 04, 2009
Don Wrote:
> Since D supports the very nice .re syntax, there's really no reason to define cast(real) at all. z.re is better in 100% of use cases. There are *NO* use cases for cast(real); any use of cast(real) is a bug. We should kill it.

I am with you. Making the language tidy is the way to go :-) Thank you for your work, and keep improving things when you can.

Bye,
bearophile
May 04, 2009
Don wrote:
> Walter Bright wrote:
>> Don wrote:
>>> I don't think anyone expects to be able to divide an integer by an imaginary, and then assign it to an integer. I was astonished that the compiler accepted it.
>>
>> There actually is a reason - completeness. Mathematically, there is a definite answer to it, so why not fill in all the entries for all the combinations?
> 
> In the case complex = int/complex, there's no problem. It's the case int = int/complex that doesn't make sense.
> And that's fundamentally because cast(real)(2 + 3i) doesn't have a definite answer.
> There's no mathematical operation to convert a complex number to a real.
> Normally, to get a real result, you need to do something like multiplying by the complex conjugate. You CAN take the real part of a complex number, and you can also take the modulus (which makes a lot of sense if you're using polar represantation).
> 
> Of course, cast() is not a mathematical operation, it's a D operation, so we can define it however we like. But defining it
> cast(real)z = z.re;
> is an arbitrary decision, which introduces lots of complexity to the language, and the many compiler bugs are a consequence.
> It's confusing for newbies (there's been questions on D.learn asking, "I can get the real part of a complex number with cast(real), but how do I get the imaginary part? cast(ireal) gives me an ireal, not a real!").
> 
> Since D supports the very nice .re syntax, there's really no reason to define cast(real) at all. z.re is better in 100% of use cases. There are *NO* use cases for cast(real); any use of cast(real) is a bug. We should kill it.

Walter, could the error message then include "Please use z.re or z.im to get the parts, instead of a cast." or something. Otherwise we have to explain to all users separately what's going on, /and/ that the omission of implementing the cast in D isn't an omission.
May 04, 2009
On Mon, 04 May 2009 10:56:41 +0200, Frits van Bommel wrote:

> Don wrote:
>> In the case complex = int/complex, there's no problem. It's the case int
>> = int/complex that doesn't make sense.
>> And that's fundamentally because cast(real)(2 + 3i) doesn't have a
>> definite answer.
> 
> Devil's advocate: one could argue it's the same as cast(int) of a float -- it returns the closest representable value, for some definition of "closest".

Hmmm. not quite because we can not do ...

   int i = floatnum.integer;

but we can do

   real r = complexnum.re;

-- 
Derek Parnell
Melbourne, Australia
skype: derek.j.parnell
May 04, 2009
Frits van Bommel Wrote:

> Don wrote:
> > In the case complex = int/complex, there's no problem. It's the case int
> > = int/complex that doesn't make sense.
> > And that's fundamentally because cast(real)(2 + 3i) doesn't have a
> > definite answer.
> 
> Devil's advocate: one could argue it's the same as cast(int) of a float -- it returns the closest representable value, for some definition of "closest".

I consider this to be a much different case. Assuming the float to an int results on approximately the same number... And everyone knows what to expect. It's also possible to convert back to float and still have approximately the same number.

For complex to int, the same things are not true. Conversion to float and then back to real can give a much different number... And not just for overflow, but normal uses of complex numbers. It's also unclear to mathematically inclined folk like me and Don what the conversion is supposed to do. A magnitude makes as much, if not more, sense.

Adding to all this, there are easier, shorter, and clearer ways to do all of these conversions, and you _have_ to agree with Don ;)
May 04, 2009
Georg Wrede wrote:
> Walter, could the error message then include "Please use z.re or z.im to get the parts, instead of a cast." or something. Otherwise we have to explain to all users separately what's going on, /and/ that the omission of implementing the cast in D isn't an omission.

If we cut the limb, why first apply an ointment to it?

Andrei
May 04, 2009
On Mon, 04 May 2009 09:44:18 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> Georg Wrede wrote:
>> Walter, could the error message then include "Please use z.re or z.im to get the parts, instead of a cast." or something. Otherwise we have to explain to all users separately what's going on, /and/ that the omission of implementing the cast in D isn't an omission.
>
> If we cut the limb, why first apply an ointment to it?

I think the point is relevant to D1, where you can't really remove the limb, but you can apply make-up to make it look less deformed ;)

-Steve
June 12, 2011
On 04/30/2009 02:20 PM, Don wrote:
> D currently allows some conversions between complex/imaginary types and
> real types, which are highly dubious.
>
> Given creal z, ireal y, these casts are legal:
> real x = cast(real)z;
> x = cast(real)y; // always sets x==0, regardless of the value of y.
> But I believe that should not be legal.
>
> For the first case, it should be written as: real x = z.re;
> (which is shorter and clearer), and the second case is probably a bug,
> and should be x = y.im; (unless the intention really was to set x=0!).
>
> By the same logic, we could have sqrt(-1)==0, since the real part is 0.
>
> The most important effect of disallowing these casts would be to fix a
> host of bugs and wierd behaviour. All the A op= B operations involve a
> cast to A. If those nonsensical casts become illegal, the nonsensical
> op= operations become illegal automatically.
>
> Eg, ireal y;
> y *= y; // mathematically nonsense, y*y is real, so can't be stored in a
> pure imaginary type!
>
> There are a few segfault/ICE bugs (eg 718, 2839) which involve
> int/=complex, an operation which never makes any sense anyway.
>
> I think we're just making problems for ourselves by allowing these
> useless operations. I think they should be killed.
> Does anyone object? (If not, I'll create a patch to do it; it's not very
> difficult).

Don, instead of this fix, could be at best phase everything built-in about complex out?

Thanks,

Andrei
June 12, 2011
Andrei:

> Don, instead of this fix, could be at best phase everything built-in about complex out?

Two usages of complex numbers in the RosettaCode site.

Acommon place where you find complex numbers is to plot  Mandelbrot/Julia sets: http://rosettacode.org/wiki/Mandelbrot_set#D

import std.stdio, std.math;

void main() {
    enum maxIter = 1000;
    foreach (y; -39 .. 39) {
        foreach (x; -39 .. 39) {
            auto c = y/40.0 - 0.5 + x/40.0i,
                 z = 0.0 + 0.0i,
                 i = 0;
            for (; i < maxIter && abs(z) < 4; i++)
                z = z ^^ 2 + c;
            write(i == maxIter ? '#' : ' ');
        }
        writeln();
    }
}



Version using std.complex:

import std.stdio, std.complex;

void main() {
    enum maxIter = 1000;
    foreach (y; -39 .. 39) {
        foreach (x; -39 .. 39) {
            auto c = Complex!double(y/40.0 - 0.5, x/40.0),
                 z = Complex!double(0, 0),
                 i = 0;
            for (; i < maxIter && z.abs() < 4; i++)
                z = z ^^ 2 + c;
            write(i == maxIter ? '#' : ' ');
        }
        writeln();
    }
}


I think it's worth adding to std.complex module few examples of usage, plus a complex() function so you are allowed to write (the imaginary part defaults to zero if the real part is set to zero):
auto z = complex(5);

Instead of:
auto z = Complex!double(5, 0);


complex(5) probably has to create a Complex!double and not a Complex!int (that is not even supported, std.complex.Complex support floating point values only).

---------------------------

Another common usage of D complex numbers is to represent generic 2D points. http://rosettacode.org/wiki/Constrained_random_points_on_a_circle#D

Original code (to plot some points in a circle):

import std.stdio, std.random, std.math;

void main() {
    char[31][31] table = ' ';

    foreach (i; 0 .. 100) {
        int x, y;
        do {
            x = uniform(-15, 16);
            y = uniform(-15, 16);
        } while(abs(12.5 - abs(x + y * 1i)) > 2.5);
        table[x + 15][y + 15] = '*';
    }

    foreach (row; table)
        writeln(row);
}


Version using std.complex:

import std.stdio, std.random, std.complex, std.math;

void main() {
    char[31][31] table = ' ';

    foreach (i; 0 .. 100) {
        int x, y;
        do {
            x = uniform(-15, 16);
            y = uniform(-15, 16);
        } while(abs(12.5 - Complex!double(x, y).abs) > 2.5);
        table[x + 15][y + 15] = '*';
    }

    foreach (row; table)
        writeln(row);
}

---------------------------

Regarding the printing of Complex values, this program:

import std.stdio, std.complex;
void main() {
    writeln(Complex!double(0, -1));
}


Prints:
0-1i


While Python prints the same complex number as:

>>> 0-1j
-1j

Bye,
bearophile
June 13, 2011
On 12/06/2011 23:37, bearophile wrote:
> Andrei:
>
>> Don, instead of this fix, could be at best phase everything
>> built-in about complex out?
>
> Two usages of complex numbers in the RosettaCode site.
>
> Acommon place where you find complex numbers is to plot
> Mandelbrot/Julia sets: http://rosettacode.org/wiki/Mandelbrot_set#D
>
> import std.stdio, std.math;
>
> void main() { enum maxIter = 1000; foreach (y; -39 .. 39) { foreach
> (x; -39 .. 39) { auto c = y/40.0 - 0.5 + x/40.0i, z = 0.0 + 0.0i, i =
> 0; for (; i<  maxIter&&  abs(z)<  4; i++) z = z ^^ 2 + c; write(i ==
> maxIter ? '#' : ' '); } writeln(); } }
>
>
>
> Version using std.complex:
>
> import std.stdio, std.complex;
>
> void main() { enum maxIter = 1000; foreach (y; -39 .. 39) { foreach
> (x; -39 .. 39) { auto c = Complex!double(y/40.0 - 0.5, x/40.0), z =
> Complex!double(0, 0), i = 0; for (; i<  maxIter&&  z.abs()<  4; i++)
> z = z ^^ 2 + c; write(i == maxIter ? '#' : ' '); } writeln(); } }
>
>
> I think it's worth adding to std.complex module few examples of
> usage, plus a complex() function so you are allowed to write (the
> imaginary part defaults to zero if the real part is set to zero):
> auto z = complex(5);

I seemed to think the plan for complex numbers was to do what happened with associative arrays, that is, keep the language syntax, but have the feature implemented in the library. Is this not the case?

-- 
Robert
http://octarineparrot.com/
June 13, 2011
On Mon, 13 Jun 2011 12:36:27 +0100, Robert Clipsham wrote:

> On 12/06/2011 23:37, bearophile wrote:
>> Andrei:
>>
>>> Don, instead of this fix, could be at best phase everything built-in about complex out?
>>
>> Two usages of complex numbers in the RosettaCode site.
>>
>> Acommon place where you find complex numbers is to plot Mandelbrot/Julia sets: http://rosettacode.org/wiki/Mandelbrot_set#D
>>
>> import std.stdio, std.math;
>>
>> void main() { enum maxIter = 1000; foreach (y; -39 .. 39) { foreach (x;
>> -39 .. 39) { auto c = y/40.0 - 0.5 + x/40.0i, z = 0.0 + 0.0i, i = 0;
>> for (; i<  maxIter&&  abs(z)<  4; i++) z = z ^^ 2 + c; write(i ==
>> maxIter ? '#' : ' '); } writeln(); } }
>>
>>
>>
>> Version using std.complex:
>>
>> import std.stdio, std.complex;
>>
>> void main() { enum maxIter = 1000; foreach (y; -39 .. 39) { foreach (x;
>> -39 .. 39) { auto c = Complex!double(y/40.0 - 0.5, x/40.0), z =
>> Complex!double(0, 0), i = 0; for (; i<  maxIter&&  z.abs()<  4; i++) z
>> = z ^^ 2 + c; write(i == maxIter ? '#' : ' '); } writeln(); } }
>>
>>
>> I think it's worth adding to std.complex module few examples of usage,
>> plus a complex() function so you are allowed to write (the imaginary
>> part defaults to zero if the real part is set to zero): auto z =
>> complex(5);
> 
> I seemed to think the plan for complex numbers was to do what happened with associative arrays, that is, keep the language syntax, but have the feature implemented in the library. Is this not the case?

That was my understanding as well, which is why I never added a complex() helper function.

It is, however, my humble opinion that we should just get rid of the complex literals.  (Otherwise, std.complex would have to be moved into druntime.)

-Lars