Skip to content

Commit c799e39

Browse files
authored
Merge pull request #254 from olekscode/221-1e10---1e-10i-sqrt-does-not-work
Fixed #221. The problem with complex sqrt
2 parents 1597a00 + 45bbc54 commit c799e39

File tree

2 files changed

+113
-9
lines changed

2 files changed

+113
-9
lines changed

src/Math-Complex/PMComplex.class.st

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,21 @@ PMComplex >> asComplex [
403403
^self
404404
]
405405

406+
{ #category : #comparing }
407+
PMComplex >> closeTo: aNumber precision: aPrecision [
408+
"A complex number self is close to a complex number other with precision epsilon if both
409+
self real is close to other real with precision epsilon and
410+
self imaginary is close to other imaginary with precision epsilon.
411+
412+
In case aNumber is real and not complex, we convert it to a complex number
413+
other = aNumber + 0i"
414+
| other |
415+
other := aNumber asComplex.
416+
417+
^ (real closeTo: other real precision: aPrecision) and: [
418+
imaginary closeTo: other imaginary precision: aPrecision ].
419+
]
420+
406421
{ #category : #arithmetic }
407422
PMComplex >> complexConjugate [
408423

@@ -704,15 +719,18 @@ PMComplex >> sinh [
704719

705720
{ #category : #'mathematical functions' }
706721
PMComplex >> sqrt [
722+
"Return the square root of the receiver with a positive imaginary part.
723+
Implementation based on: https://en.wikipedia.org/wiki/Complex_number#Square_root"
707724

708-
"Return the square root of the receiver with a positive imaginary part."
709-
710-
| u v |
711-
(imaginary = 0 and: [ real >= 0 ]) ifTrue: [
712-
^ self class real: real sqrt imaginary: 0 ].
713-
v := (self abs - real / 2) sqrt.
714-
u := imaginary / 2 / v.
715-
^ self class real: u imaginary: v
725+
| newReal newImaginary imaginarySign |
726+
727+
imaginarySign := imaginary sign = 0
728+
ifTrue: [ 1 ] ifFalse: [ imaginary sign ].
729+
730+
newReal := ((self abs + real) / 2) sqrt.
731+
newImaginary := imaginarySign * ((self abs - real) / 2) sqrt.
732+
733+
^ newReal + newImaginary i
716734
]
717735

718736
{ #category : #'mathematical functions' }

src/Math-Tests-Complex/PMComplexTest.class.st

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,39 @@ PMComplexTest >> testBug1 [
193193
self assert: (0.5 * (2 + 0 i) ln) exp equals: (0.5 * 2 ln) exp
194194
]
195195

196+
{ #category : #'testing - close to' }
197+
PMComplexTest >> testCloseTo [
198+
199+
self assert: 2 + 3.000000000000001i closeTo: 2 + 3i.
200+
self assert: 2.000000000000001 + 3i closeTo: 2 + 3i.
201+
self assert: 2.000000000000001 + 3.000000000000001i closeTo: 2 + 3i.
202+
]
203+
204+
{ #category : #'testing - close to' }
205+
PMComplexTest >> testCloseToReal [
206+
207+
self assert: 2 + 0.000000000000001i closeTo: 2.
208+
self assert: 2.000000000000001 + 0.000000000000001i closeTo: 2.
209+
210+
self assert: 2 + 0i closeTo: 2.000000000000001.
211+
self deny: 2 + 3i closeTo: 2.000000000000001
212+
]
213+
214+
{ #category : #'testing - close to' }
215+
PMComplexTest >> testCloseToRealWithPrecision [
216+
217+
self assert: 2 + 0.001i closeTo: 2 precision: 0.01.
218+
self assert: 2.001 + 0.001i closeTo: 2 precision: 0.01.
219+
]
220+
221+
{ #category : #'testing - close to' }
222+
PMComplexTest >> testCloseToWithPrecision [
223+
224+
self assert: 2 + 3.001i closeTo: 2 + 3i precision: 0.01.
225+
self assert: 2.001 + 3i closeTo: 2 + 3i precision: 0.01.
226+
self assert: 2.001 + 3.001i closeTo: 2 + 3i precision: 0.01.
227+
]
228+
196229
{ #category : #tests }
197230
PMComplexTest >> testComplexAsComplex [
198231
| ineg |
@@ -472,6 +505,21 @@ PMComplexTest >> testNew [
472505
self assert: c imaginary equals: 0
473506
]
474507

508+
{ #category : #'testing - close to' }
509+
PMComplexTest >> testNotCloseToRealWithPrecision [
510+
511+
self deny: 2 + 0.001i closeTo: 2 precision: 0.000001.
512+
self deny: 2.001 + 0.001i closeTo: 2 precision: 0.000001.
513+
]
514+
515+
{ #category : #'testing - close to' }
516+
PMComplexTest >> testNotCloseToWithPrecision [
517+
518+
self deny: 2 + 3.001i closeTo: 2 + 3i precision: 0.000001.
519+
self deny: 2.001 + 3i closeTo: 2 + 3i precision: 0.000001.
520+
self deny: 2.001 + 3.001i closeTo: 2 + 3i precision: 0.000001.
521+
]
522+
475523
{ #category : #tests }
476524
PMComplexTest >> testNumberAsComplex [
477525
| minusOne |
@@ -646,7 +694,7 @@ PMComplexTest >> testSquareRootOfNegativePureImaginaryNumberIsAComplexNumberWith
646694

647695
squareRoot := pureImaginaryNumber sqrt.
648696

649-
expected := 2 sqrt negated + 2 sqrt i.
697+
expected := 2 sqrt - 2 sqrt i.
650698
self assert: squareRoot real closeTo: expected real.
651699
self assert: squareRoot imaginary closeTo: expected imaginary
652700

@@ -681,6 +729,44 @@ PMComplexTest >> testSquareRootOfPositiveRealNumberIsAComplexNumberWithOnlyAReal
681729
self assert: squareRoot equals: expected
682730
]
683731

732+
{ #category : #tests }
733+
PMComplexTest >> testSquareRootOfVeryLargeRealAndVerySmallImaginary [
734+
"This may cause problems because of the loss of precision.
735+
Very large and very small floating point numbers have different units of least precision.
736+
737+
verySmall ~= 0.
738+
veryLarge + verySmall = veryLarge."
739+
740+
| veryLarge verySmall complexNumber expected |
741+
742+
veryLarge := 1e20.
743+
verySmall := 1e-20.
744+
745+
complexNumber := veryLarge + verySmall i.
746+
expected := 1e10 + 1e-31 i.
747+
748+
self assert: complexNumber sqrt closeTo: expected
749+
]
750+
751+
{ #category : #tests }
752+
PMComplexTest >> testSquareRootOfVerySmallRealAndVeryLargeImaginary [
753+
"This may cause problems because of the loss of precision.
754+
Very large and very small floating point numbers have different units of least precision.
755+
756+
verySmall ~= 0.
757+
veryLarge + verySmall = veryLarge."
758+
759+
| veryLarge verySmall complexNumber expected |
760+
761+
veryLarge := 1e20.
762+
verySmall := 1e-20.
763+
764+
complexNumber := verySmall + veryLarge i.
765+
expected := 7071067811.865476 + 7071067811.865475 i.
766+
767+
self assert: complexNumber sqrt closeTo: expected
768+
]
769+
684770
{ #category : #tests }
685771
PMComplexTest >> testSquareRootOfZeroIsZero [
686772
"comment stating purpose of instance-side method"

0 commit comments

Comments
 (0)