# [Snippet] Complex Numbers

#### looking_for_help

Level 14
Keep seven digits, like in my Maths library.

Level 18
Ok thanks

#### edo494

Level 23

```private constant real e = 2.71828182845904523536028747135266249775724700000 ```

This is bad and you shouldn't do that. However, the explanation why it is bad was incorrect. Its not slowing down your resource, but just making the variable to overflow and therefore store an undefined value.

As already mentioned, Wc3 reals are 32 bit floats. This datatype has seven significant digits, which means that if you put in more, it will overflow. Try this:

JASS:
``````private static method onInit takes nothing returns nothing
local real e = 2.71828182845904523536028747135266249775724700000
call BJDebugMsg(R2S(e*e))
endmethod``````

For me, it outputs 4.00, while the correct result should be about 7.38.

Thats the reason why I use in my Maths library the constant `real E = 2.718282` (seven digits). It is a bit more accurate than the native Jass constant for e, `bj_E` (six digits), thats why I included that constant in the Maths library. Higher accuracy is not possible with 32 bit floating numbers.

If you want to use a custom constant for e, use that one. Don't use any higher "accuracy", because it will make your variable overflow.

precision is 8, not 7 characters

JASS:
``````function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
local real r = 123456789.0123456789
call BJDebugMsg(R2S(r))
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions )
endfunction``````

prints 123456784.000

Level 18
Yes so 2.7182818

#### looking_for_help

Level 14
precision is 8, not 7 characters

No.

prints 123456784.000

This is not an appropriate test to verify such a thing. Just because the number gets displayed correct, it doesn't mean you really gained anything.

Float has log(2^(23 + 1)) = 7.22 significant bits. The plus one comes from the normalization of the mantissa, from which you can save one extra, the so-called "hidden" bit. However, you don't really have eight significant bits but something between seven and eight. So what does that mean? That means that you don't have enough space to store every number with eight significant bits, but only about 25% of them. However, this is implementation dependent and nobody can guarantee you that you (accidentaly) hit a number which gets stored like this, if this was even implemented at all.

So, what to do in our case which is Warcraft 3? Just lets take a look at a simple example: Compute e^(11):

JASS:
``````private static method onInit takes nothing returns nothing
local real e1 = 2.7182818
local real e2 = 2.718282

call BJDebugMsg(R2S(e1*e1*e1*e1*e1*e1*e1*e1*e1*e1*e1))
call BJDebugMsg(R2S(e2*e2*e2*e2*e2*e2*e2*e2*e2*e2*e2))
endmethod``````

Displays:

59874.102
59874.156

while the real value is about 59874.14172. As you can easily see, the second value is closer to the real result, although the constant of e which was used for the calculation has one digit less.

Conclusion: Take the seven digit number

Last edited:

#### Nestharus

Level 31
Why are you using R2S?... I can't take anything you say seriously when you aren't using R2SW, lol.

#### looking_for_help

Level 14
Why are you using R2S?... I can't take anything you say seriously when you aren't using R2SW, lol.

Maybe because it doesn't change anything?

If you take a look at the numbers I posted, they have themself already eight significant digits, so I don't need any further "accuracy" by R2SW.

If I use R2SW I get the following output:

59874.101562...
59874.156250...

Does this change anything? I don't think so -.-
The rest of the digits is just useless garbage.

The R2SW function is for displaying digits after the decimal point. If you have sufficient digits before the decimal point (like in this example) it doesn't make sense to use that function at all.

#### Malhorne

Level 18
Okay thank you looking_for_help for clarifing this !
I'll change the number of digits

#### edo494

Level 23
Float has log(2^(23 + 1)) = 7.22 significant bits. The plus one comes from the normalization of the mantissa, from which you can save one extra, the so-called "hidden" bit. However, you don't really have eight significant bits but something between seven and eight. So what does that mean? That means that you don't have enough space to store every number with eight significant bits, but only about 25% of them. However, this is implementation dependent and nobody can guarantee you that you (accidentaly) hit a number which gets stored like this, if this was even implemented at all.

nice explanation, I was indeed wrong, but I didnt know the formula exactly, so couldnt really verify, but now I know it

#### Waffle

Level 6
I would suggest at this point we have concluded:
Warcraft does NOT treat reals as string... ever. (as was to be expected imho..)

Can we please now stop making such strong claims without reference. how could such absurdities even live this long..

#### Malhorne

Level 18
Yep you're right on that one

#### Menag

Level 6
Why you didn't add the possibility for z1^z2? Isn't much more work then z^r...

#### Malhorne

Level 18
Yep I forgot this thank you for reminding me to do this ^^

Unfortunately my computer is pretty dead...
So I'm using another one without JNGP or my custom things so I can't edit anything for the moment. (Still the computer I'm using isn't mine)

Level 18
Update

#### Menag

Level 6
JASS:
``````static method div takes thistype c1, thistype c2 returns thistype
local thistype c3 = thistype.allocate()
local real dv = c2.re*c2.re + c2.im*c2.im
if (dv!=0) then
set c3.re = (c1.re*c2.re + c1.im*c2.im)/dv
set c3.im = (c1.im*c2.re - c1.re*c2.im)/dv
else
set c3.re = c1.re*c2.re + c1.im*c2.im
set c3.im = c1.im*c2.re - c1.re*c2.im
endif
return c3
endmethod``````

Returning a number when dividing by 0 is somehow... stupid.

JASS:
``````        method operator arg takes nothing returns real
return Atan(this.im/this.re)
endmethod``````

This is actually wrong. Atan is not enough to calculate the argument. Atan returns negative values, while the argument is defined as the smallest positive number. Then you should also obtain the correct values for the logarithm if you use the prinipal branch.

JASS:
``````        static method sqrt takes thistype c returns thistype
local thistype c2 = thistype.allocate()
if c.im==0 then
set c2.re = SquareRoot(c.re)
set c2.im = 0
else
set c2.re = SquareRoot((c2.re + SquareRoot(c.re*c.re + c.im*c.im))/2)
set c2.im = SquareRoot((c2.re - SquareRoot(c.re*c.re + c.im*c.im))/2)
if c.im < 0 then
set c2.im = -c2.im
endif
endif
return c2
endmethod``````

This does not make any sense. You use c2.re for calculating the square root of c.re, although c2.re is not initialised yet. I think you wanted to use c.re there.
For the imaginary part, you have to calculate sqrt((abs(c) - c.re)/2 and not c.re - abs(c).

#### Malhorne

Level 18
Oh sorry for the first one, a mistake of mine.

On the second the argument is the value between ]-pi;pi]
EDIT : Fixed properly

On the third I'll fix the c2 into c my bad.
But I actually got this :
JASS:
``````set c2.re = SquareRoot((c.re + SquareRoot(c.re*c.re + c.im*c.im))/2)
set c2.im = SquareRoot((c.re - SquareRoot(c.re*c.re + c.im*c.im))/2)``````

#### Menag

Level 6
For a non-real complex number, the abs(c) > re(c). Therefore
(c.re - SquareRoot(c.re*c.re + c.im*c.im) < 0.
Thus, it should actually be
(SquareRoot(c.re*c.re + c.im*c.im - c.re) - c.re).

Last edited:

#### Malhorne

Level 18
Oh yes...
I think there is somethign wrong with it.
I'll redo it later.
For now I'll stick with the one you wrote.

#### Menag

Level 6
JASS:
``````        method operator arg takes nothing returns real
if this.im>0 then
return pi/2 - Atan2(this.im, this.re)
else
return -pi/2 - Atan2(this.im, this.re)
endif
endmethod``````

I don't understand this. Just go for Atan2(this.im, this.re) and return it. Then you have a range of (-pi,pi], which should be fine, even for the logarithm. (Ignore the stuff i said about it before, just exchange Atan with Atan2.)

#### Sapeur-Goblin

Level 3
Any complex number (=/= 0) has only 2 roots (and not 4...)

Level 15
Interesting...

Now I wonder for a really specific case of this in WC3 maps.

#### Menag

Level 6
Any complex number (=/= 0) has only 2 roots (and not 4...)

Did anyone say something different?

#### edo494

Level 23
the API is really bad, all your methods are static, but pretty much all of them require "thistype" parameter, change them to nonstatic, and drop the argument, so you can actually use this as struct

#### Sapeur-Goblin

Level 3
Did anyone say something different?

Yes :
static method sqrt takes thistype c returns thistype
* ~ It returns sqrt(c)
* ~ It returns the SquareRoot I choose (because there is actually four squareRoots : two with negative
* imaginary parts and two with positive imaginary parts).
* Basically you'll never have any use of this I think it is more nerdy than anything.

#### Malhorne

Level 18
@edo : so :
JASS:
``````Complex c1 = Complex.create()
Complex c2 = Complex.create()
c1.re = 1
c1.im = 1
c2.re = 2
c2.im = 2

Is less good than :
JASS:
``````Complex c1 = Complex.create()
Complex c2 = Complex.create()
c1.re = 1
c1.im = 1
c2.re = 2
c2.im = 2
??

It is not better.
Struct != Class.
If I could use a C like struct I would have done it long ago.
If there were operators +, -, *, / I would have done this.
But there is not.

@Sapeur -> Wtf was I thinking when I wrote this ?

#### edo494

Level 23
I think Complex.add(c1, c2) is bad, since Complex already stores the member data, so why not use it as member function, but as static function is beyond me.

and I dont know what you mean by Struct != Class, this is both opinion-based and language-based.

and to me you are using this like C-like struct, very much, with the difference that the functions arent free-standing, but are defined as static, which is almost indifferent, but instead of Complex_ you write "Complex.".

What I mean by chainging the API(since you maybe didnt get it) is also that you dont want to invoke some owning member, but you want to take c1, and add c2 to it, and return the Complex value, so Complex.add(c1, c2) is actually very much counter-intuitive

#### Malhorne

Level 18
It adds nothing and it is not more intuitive...
Or at least I find it bad.
When I made it with non static methods it was like a mess to use, so I chose to put only static method.
If you think putting static method is a shame for OOP I'll change it to function and we'll get an underscore which is even worst.

Struct -> Stores multiple data
Class -> Add functions to manipulate those datas.
You've got both in C++, that's why it is not preference or anything.

#### edo494

Level 23
The only difference between struct and class in C++ is the initial visibility(class private, struct public), so no your description is not fitting C++ at least.

And no I dont say to change them to functions, after all, you are the creator, not me, so they will be as you will make them.

#### Malhorne

Level 18
You can't add methods for struct you have to define them as functions.
There is no constructor for struct.
There is not destructor for struct.
So basically yes it fits C++.
Now I think we should stop discussing about that anymore on this thread there is already a lot of off-topic.

And to bring back the API subject I prefer it this way and I'll keep it this way, and if people wants the API to be non static, go gy this...

#### edo494

Level 23
Disproof, another disproof so no, they are the same as I said.

And no, I just offered it, you didnt like it, thats fine

#### Malhorne

Level 18
Ok.

Basically sorry for my behavior yesterday had some troubles...

Level 23

#### Malhorne

Level 18
Yeah but I behave like a capricious child and it doesn't reflect me that's why I wanted to apologize.
[Off-Topic Off].

#### looking_for_help

Level 14
I like that this is a quite small library with most of the functionality needed.

However, I think the design could be improved. At the moment it requires quite some code to do basic things. Say I want to compute `c3*(c1 + c2)` and output it as string, I have to do this:

JASS:
``````local Complex c1 = Complex.create()
local Complex c2 = Complex.create()
local Complex c3 = Complex.create()
set c1.re = 1
set c1.im = -3
set c2.re = 1.5
set c2.im = -4
set c3.re = 2
set c3.im = 1

Now, with instance methods and constructor arguments:

JASS:
``````local Complex c1 = Complex.create(1, -3)
local Complex c2 = Complex.create(1.5, -4)
local Complex c3 = Complex.create(2, 1)

Cleaner, easier to read and to understand, right? As a Complex number has to be initialized anyways, it makes sense to restrict construction to explicitly initialized types.

An instance makes always sense if the object you want to model carries a state. A File has state (location within the file system, size, security attributes etc.) and is therefore typically modelled as an "instance" (== non-static) class. A Math function like Sine or Cosine has no state, because it works without any additional data and is therefore a good candidate for a static class or a free function, which typically also doesn't have any internal state.

A complex number has a state, namely its real and imaginary part. Therefore it makes sense to design it as a non-static class that uses instance methods. The computation in the code above also shows that this makes sense from a coding point of view.

And since you already do have instance methods like toString or abs, it would definitly make sense to adopt that to the other methods as well.

#### edo494

Level 23
how is that relevant to anything that ever happened in this thread?

#### Magtheridon96

Level 35
I don't think I need to add to your nonsensical arguing, I'm suggesting something for the resource.

#### edo494

Level 23
elitism at its prime?

So how would manderbrot usable in wc3 in any sensible way anyway?

#### Malhorne

Level 18
You will laugh I though of Julia Set when I made this.
Anyway I'll implement this idea of the constructor.
I was hesitating but yeah you're right.

Level 18
Okay updated.

#### looking_for_help

Level 14
Okay updated.

So, could you give a rationale why the resource still uses static methods over instance methods, when instance methods provide a shorter and more readable syntax?

Especially since you use some instance methods, this is not really consistent at the moment. Why are some methods static and some (like toString) not?

Static methods make sense if you can perform an action associated with its class without an actual object of the class itself. You can't perform an addition or subtraction of two complex values without having two complex objects in the first place. Therefore either instance methods or free functions would be more appropriate. However, since we have no function/method overloading in vJass, instance methods are preferable.

Btw, is there a specific reason why the default instance limit of this is 10000? Its fine to provide a constant to modify this value, but I think the default value should be the vJass default value of 8190, since this is most likely sufficient anyways.

#### Malhorne

Level 18
I don't really agree on the more readable or at least I find it so ugly.
You're right for the 10000.

#### looking_for_help

Level 14
Alright, you made a small typo in the constructor, therefore it does not compile at the moment:

`static method create takes real r, real i return thistype`

->

`static method create takes real r, real i returns /* <- */ thistype`

EDIT:

It seems there is something wrong with the sqrt implementation... This code

JASS:
``````local Complex c = Complex.create(4, -1.5)
call BJDebugMsg(Complex.sqrt(c).toString()) // Output: 2.034 - 1.374i``````

outputs 2.034 - 1.374i, while the correct result is 2.034 - 0.369i. You might want to take a look at this.

#### Malhorne

Level 18
Oups.
Thanks for pointing it.
I'll check it tomorrow thanks for it.

EDIT : Fixed.

I can't really fix my mind on static and non static.
Okay .add .sub .mul .div sounds good.
But what about .conj ? Static/Non static ? Returns or intern ?
I can't really think of a way to unify it nicely.

Last edited:

#### looking_for_help

Level 14
I can't really fix my mind on static and non static.
Okay .add .sub .mul .div sounds good.
But what about .conj ? Static/Non static ? Returns or intern ?
I can't really think of a way to unify it nicely.

Well, it was more a recommendation than a requirement.

If you really don't like the way with instance methods, its ok too. Its just that I prefer a short syntax, especially since vJass is already quite verbose. And repeating `Complex.` for every operation increases verbosity.

However, thats just my own opinion, its not a neccessary thing to change.

If you do change it, it would be good if its consistent. There is nothing wrong with making all methods instance methods, .conj is not really different than the other methods (you can only compute the conjugate of a complex number if you already have one, so an instance is required anyways).

#### Malhorne

Level 18
I mean for the .conj do I make it return a number or I make it returns nothing :/

#### looking_for_help

Level 14
I would return `thistype` after doing the modification, because then you can concatenate actions:

JASS:
``````local Complex c1 = Complex.create(1, 2)

#### Malhorne

Level 18
Should I make a warning for a code like this :
JASS:
``````local Complex c = Complex.create(4, -1.5)
local Complex c2 = Complex.create(2, 2)
call BJDebugMsg(I2S(c))``````
It leaks a struct :s

#### looking_for_help

Level 14
No, that should be obvious/would not be the job of such a library.

Replies
11
Views
2K
Replies
43
Views
5K
Replies
13
Views
2K
Replies
8
Views
2K
Replies
19
Views
9K