Getting the imaginary part of an imaginary number

There is a slight discrepancy (in language and hence meaning) between Chapel and C with how they handle conversions between imaginary types and other types. The is related to how Chapel and C approach casting (or coercion). It might be a trap for C users and is worth noting.

The way that Chapel does things is perfectly sound and robust and no change is even hinted at. This is just a note. A simpler form of this brief discussion might usefully appear in the documentation at the appropriate place.

Given an imag(w) number t with an imaginary component y is a floating point number, i.e. a real(w), then one can write mathematically

t = i y

To create t as a const or var in Chapel, one can assign to t the value of i y with the cast (coercion)

t = y:imag(w);

Similarly, one can retrieve the original y with the cast (coercion)

y = t:real(w);

Note that one can also say

assert(y == abs(t));

Note too that

assert(y == (y:imag(w)):real(w));

This is somewhat different to the C language standard which says:

  1. When a value of imaginary type is converted to a real type other than bool , the result is a positive zero.
  2. When a value of real type is converted to an imaginary type, the result is a positive imaginary zero.
This explanation may need refinement.

Hi Damian -

That's right. I think there are other ways to create an imaginary number, but they might end up actually adding a multiply in the generated code. (Of course we could fix that with an overload to specialize multiplying by a param value of 1.0i, but AFAIK we have not done that yet).

At some point in history, the cast from a real value to an imag value did result in 0.0i (and vice versa). However, this meant that imaginary numbers had to be more special than other types when writing generic code. One case that came up was, when reading a floating point number, we wanted to implement it simply as reading a decimal number and then casting to the appropriate type. This was done in PR Fix cast real imag by mppf · Pull Request #4029 · chapel-lang/chapel · GitHub which also has some rationale.

... this behavior [producing 0] has been surprising to several Chapel developers.
Furthermore, since it is an explicit cast, it seems that there is a low
likelihood of surprise from any inconsistency (the new inconsistency is
that it doesn't produce the same output as casting from real to complex
and then to imag).

In any case I agree that this is something that should be clear in the documentation. Here I think the TODO is to update Conversions — Chapel Documentation 1.33.

1 Like

I could be wrong but I think that is too late, Where you define all the types, you need examples of their declaration to provide a better context. And that wll inevitably involve conversions, albeit simple ones.

That gives people the flavur quickly (and from there you can then refer the reader to the section on conversions or other sections for more detail).

What do you think?

Also, while all types are supported by numBits(), only complex(w) has the .bits method to return the same result as numBits(). Is this an oversight or a historic anachronism?

Oversight is a bad word. Something on the TO DO list is better description

I could be wrong but I think that is too late, Where you define all the types, you need examples of their declaration to provide a better context. And that wll inevitably involve conversions, albeit simple ones.

Is mentioning the potential to cast between real/imag in Types — Chapel Documentation 1.33 what you have in mind?

I'm not seeing .bits for complex:

var x: complex;

writeln(x.bits); // error: unresolved call 'complex(128).bits'
writeln(complex.bits); // error: unresolved call 'type complex(128).bits'

If it's in the documentation, I think we need to remove it from the spec. But I can't find it anywhere. Where did you see it?

1 Like

You said ... Is mentioning the potential to cast between real/imag in Types — Chapel Documentation 1.33 what you have in mind?

Yes.. I think in that section, you need to introduce some declarations which contain declarations with initializations for all types. What this means for the imag(w) types, is showing examples like

var y = 0.6i;
var t = s:imag; // s was previously declared as some real variable or constant

where obviously the second is a cast.

I see the .bits in the above mentioned 1.33 documentation for Types.

BTW, where is the record which defines a complex number defined? I used to be able to find it but not any more. Or maybe it is too early in the morning.

Ah, I see, you are thinking, "Let's have a short example showing the usage of each type here". That seems reasonable to me, provided these examples are pretty short.

Ah, it's something that was removed on main (so won't be in our upcoming 2.0 release).

I think we removed it because, while complex might be implemented as a record, users should view it as a primitive type. If you are looking for some information about memory layout, I think that's something we'd want to say in a different way (if we specify it at all).