Skip to content

Conversation

mihirparadkar
Copy link

Made the Complex type generic to accommodate single-precision complex numbers, added a =~operator as used in the Complex module and other outside numeric modules, and a convenience ** operator as a wrapper to math.pow, all of which also operate on complex numbers.

Mihir Paradkar added 2 commits August 28, 2016 00:51
Complex*[T] = tuple[re, im: T]
## a complex number, consisting of a real and an imaginary part
Complex128* = tuple[re, im: float64]
Complex64* = tuple[re, im: float32]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isnt it better to define Complex128 and Complex64 as Complex[float64] and Complex[float32]?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's indeed confusing.

@mihirparadkar
Copy link
Author

mihirparadkar commented Aug 29, 2016

The type converters that I put in to enable the complex numbers to play nicely with int literals and float literals of the wrong type are making it very hard to reason about the type of a result, and causing strange errors when I make the simple changes that were suggested. Furthermore, in certain cases, the code passes the Nim type checker and compiles to C/C++ successfully, but gcc throws errors from its type checker about the wrong type of struct being passed to a function.
To fix these I was thinking of making the type conversion stricter by following the conversion rules of C++'s complex (implicit complex conversion only from Complex64 to Complex128, no binary operators between int literals and complex numbers).
(from http://en.cppreference.com/w/cpp/numeric/complex/operator_arith3)

However, since the implementations of many of the functions in the module use float64 literals, I would either have to duplicate every proc with an implementation using float32 literals or prefix every literal with a conversion function (e.g. replace 1.0 with (T)(1.0)), which I'm not sure is considered proper style. Any suggestions?

As a further note, I think it's a good idea to have the imaginary constant (0.0, 1.0) exported from the module, but input would be helpful on the exact symbol to use. Examples include:
C++ like, i for Complex128, i64 for Complex64,
Julia-like, im and im64
Python-like, j and j64
R, C -like, I and I64

Your thoughts are much appreciated

@yglukhov
Copy link
Member

yglukhov commented Sep 5, 2016

@mihirparadkar, tbh I can't really understand the problem you're describing. Could you please elaborate and come up with an example of code that doesn't work when you define the types as suggested.

@mihirparadkar
Copy link
Author

mihirparadkar commented Sep 5, 2016

Right now, the following code works:

var a64: Complex64 = (1.0f, 2.0f)
var numer = 1.0
var b = numer / a64

However, the inferred type of b is Complex128, not Complex64. This is because there is no converter defined between float64 and Complex64, since there exists one between float64 and Complex128 and if the latter converter existed, the compiler would be unable to resolve whether division between a float64 and a Complex64 should convert the float64 to a Complex64 or convert both arguments to Complex128.

One fix to this is to disallow dividing float64s by Complex64s entirely, as C++ does. However, an example of a function this would break is

arcsech[T](z: Complex[T]): Complex[T]

which has the implementation

  result = ln(1.0/z+sqrt(1.0/z+1.0)*sqrt(1.0/z-1.0))

Those expressions of the form 1.0/z would cause errors if z were a Complex64. One fix to this is to replace all 1.0 with (T)(1.0), but I'm not sure that is good style. Is there a better fix to maintain generality?

@loofj
Copy link

loofj commented Oct 15, 2016

What is the rational for using ** for the operator for floats and complex numbers? math implements ^ as exponentiation operator, but right now it is only compatible with integers.

@Araq
Copy link
Member

Araq commented Nov 19, 2017

One fix to this is to replace all 1.0 with (T)(1.0), but I'm not sure that is good style.

That seems the best way to do it, yes.

result.re = s*cos(r)
result.im = s*sin(r)

proc `**` *[T](x, y: Complex[T]): Complex[T] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, Nim convention is to use ^ as exponentiation operator

@krux02
Copy link
Contributor

krux02 commented Oct 31, 2018

this is superseeded by #9590

@krux02 krux02 closed this Oct 31, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants