From a93f3d31f4107b9eaabcc299ea22c45cf221f841 Mon Sep 17 00:00:00 2001 From: Oleksandr Zaitsev Date: Sat, 23 Apr 2022 15:32:01 +0200 Subject: [PATCH 1/6] Added PMComplex >> closeTo:precision: --- src/Math-Complex/PMComplex.class.st | 15 ++++++++++ src/Math-Tests-Complex/PMComplexTest.class.st | 30 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/Math-Complex/PMComplex.class.st b/src/Math-Complex/PMComplex.class.st index 0b03f9e0..7cb4fa45 100644 --- a/src/Math-Complex/PMComplex.class.st +++ b/src/Math-Complex/PMComplex.class.st @@ -403,6 +403,21 @@ PMComplex >> asComplex [ ^self ] +{ #category : #comparing } +PMComplex >> closeTo: aNumber precision: aPrecision [ + "A complex number self is close to a complex number other with precision epsilon if both + self real is close to other real with precision epsilon and + self imaginary is close to other imaginary with precision epsilon. + + In case aNumber is real and not complex, we convert it to a complex number + other = aNumber + 0i" + | other | + other := aNumber asComplex. + + ^ (real closeTo: other real precision: aPrecision) and: [ + imaginary closeTo: other imaginary precision: aPrecision ]. +] + { #category : #arithmetic } PMComplex >> complexConjugate [ diff --git a/src/Math-Tests-Complex/PMComplexTest.class.st b/src/Math-Tests-Complex/PMComplexTest.class.st index 1170d2ce..56eb4f3a 100644 --- a/src/Math-Tests-Complex/PMComplexTest.class.st +++ b/src/Math-Tests-Complex/PMComplexTest.class.st @@ -178,6 +178,36 @@ PMComplexTest >> testBug1 [ self assert: (0.5 * (2 + 0 i) ln) exp equals: (0.5 * 2 ln) exp ] +{ #category : #tests } +PMComplexTest >> testCloseTo [ + + self assert: 2 + 3.000000000000001i closeTo: 2 + 3i. + self assert: 2.000000000000001 + 3i closeTo: 2 + 3i. + self assert: 2.000000000000001 + 3.000000000000001i closeTo: 2 + 3i. +] + +{ #category : #tests } +PMComplexTest >> testCloseToReal [ + + self assert: 2 + 0.000000000000001i closeTo: 2. + self assert: 2.000000000000001 + 0.000000000000001i closeTo: 2. +] + +{ #category : #tests } +PMComplexTest >> testCloseToRealWithPrecision [ + + self assert: (2 + 0.001i closeTo: 2 precision: 0.01). + self assert: (2.001 + 0.001i closeTo: 2 precision: 0.01). +] + +{ #category : #tests } +PMComplexTest >> testCloseToWithPrecision [ + + self assert: (2 + 3.001i closeTo: 2 + 3i precision: 0.01). + self assert: (2.001 + 3i closeTo: 2 + 3i precision: 0.01). + self assert: (2.001 + 3.001i closeTo: 2 + 3i precision: 0.01). +] + { #category : #tests } PMComplexTest >> testComplexAsComplex [ | ineg | From cee48c759cf6e8508db7b9af93d56d87d0238b8b Mon Sep 17 00:00:00 2001 From: Oleksandr Zaitsev Date: Sat, 23 Apr 2022 15:57:31 +0200 Subject: [PATCH 2/6] Fixed #221. The problem with complex sqrt --- src/Math-Complex/PMComplex.class.st | 19 +++++---- src/Math-Tests-Complex/PMComplexTest.class.st | 40 ++++++++++++++++++- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/src/Math-Complex/PMComplex.class.st b/src/Math-Complex/PMComplex.class.st index 7cb4fa45..6696052e 100644 --- a/src/Math-Complex/PMComplex.class.st +++ b/src/Math-Complex/PMComplex.class.st @@ -721,15 +721,18 @@ PMComplex >> sinh [ { #category : #'mathematical functions' } PMComplex >> sqrt [ + "Return the square root of the receiver with a positive imaginary part. + Implementation based on: https://en.wikipedia.org/wiki/Complex_number#Square_root" - "Return the square root of the receiver with a positive imaginary part." - - | u v | - (imaginary = 0 and: [ real >= 0 ]) ifTrue: [ - ^ self class real: real sqrt imaginary: 0 ]. - v := (self abs - real / 2) sqrt. - u := imaginary / 2 / v. - ^ self class real: u imaginary: v + | newReal newImaginary imaginarySign | + + imaginarySign := imaginary sign = 0 + ifTrue: [ 1 ] ifFalse: [ imaginary sign ]. + + newReal := ((self abs + real) / 2) sqrt. + newImaginary := imaginarySign * ((self abs - real) / 2) sqrt. + + ^ newReal + newImaginary i ] { #category : #'mathematical functions' } diff --git a/src/Math-Tests-Complex/PMComplexTest.class.st b/src/Math-Tests-Complex/PMComplexTest.class.st index 56eb4f3a..5f1583ef 100644 --- a/src/Math-Tests-Complex/PMComplexTest.class.st +++ b/src/Math-Tests-Complex/PMComplexTest.class.st @@ -715,7 +715,7 @@ PMComplexTest >> testSquareRootOfNegativePureImaginaryNumberIsAComplexNumberWith squareRoot := pureImaginaryNumber sqrt. - expected := 2 sqrt negated + 2 sqrt i. + expected := 2 sqrt - 2 sqrt i. self assert: squareRoot real closeTo: expected real. self assert: squareRoot imaginary closeTo: expected imaginary @@ -750,6 +750,44 @@ PMComplexTest >> testSquareRootOfPositiveRealNumberIsAComplexNumberWithOnlyAReal self assert: squareRoot equals: expected ] +{ #category : #tests } +PMComplexTest >> testSquareRootOfVeryLargeRealAndVerySmallImaginary [ + "This may cause problems because of the loss of precision. + Very large and very small floating point numbers have different units of least precision. + + verySmall ~= 0. + veryLarge + verySmall = veryLarge." + + | veryLarge verySmall complexNumber expected | + + veryLarge := 1e20. + verySmall := 1e-20. + + complexNumber := veryLarge + verySmall i. + expected := 1e10 + 1e-31 i. + + self assert: complexNumber sqrt closeTo: expected +] + +{ #category : #tests } +PMComplexTest >> testSquareRootOfVerySmallRealAndVeryLargeImaginary [ + "This may cause problems because of the loss of precision. + Very large and very small floating point numbers have different units of least precision. + + verySmall ~= 0. + veryLarge + verySmall = veryLarge." + + | veryLarge verySmall complexNumber expected | + + veryLarge := 1e20. + verySmall := 1e-20. + + complexNumber := verySmall + veryLarge i. + expected := 7071067811.865476 + 7071067811.865475 i. + + self assert: complexNumber sqrt closeTo: expected +] + { #category : #tests } PMComplexTest >> testSquareRootOfZeroIsZero [ "comment stating purpose of instance-side method" From be966bd56f99ad1f897225add829556a867ed9c5 Mon Sep 17 00:00:00 2001 From: Oleksandr Zaitsev Date: Sat, 23 Apr 2022 16:09:50 +0200 Subject: [PATCH 3/6] Added two more tests for not close to --- src/Math-Tests-Complex/PMComplexTest.class.st | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Math-Tests-Complex/PMComplexTest.class.st b/src/Math-Tests-Complex/PMComplexTest.class.st index 5f1583ef..10484ade 100644 --- a/src/Math-Tests-Complex/PMComplexTest.class.st +++ b/src/Math-Tests-Complex/PMComplexTest.class.st @@ -541,6 +541,21 @@ PMComplexTest >> testNew [ self assert: c imaginary equals: 0 ] +{ #category : #tests } +PMComplexTest >> testNotCloseToRealWithPrecision [ + + self deny: (2 + 0.001i closeTo: 2 precision: 0.000001). + self deny: (2.001 + 0.001i closeTo: 2 precision: 0.000001). +] + +{ #category : #tests } +PMComplexTest >> testNotCloseToWithPrecision [ + + self deny: (2 + 3.001i closeTo: 2 + 3i precision: 0.000001). + self deny: (2.001 + 3i closeTo: 2 + 3i precision: 0.000001). + self deny: (2.001 + 3.001i closeTo: 2 + 3i precision: 0.000001). +] + { #category : #tests } PMComplexTest >> testNumberAsComplex [ | minusOne | From 188e2b1cd0fc6dc7700203649befa5f8d1c771b2 Mon Sep 17 00:00:00 2001 From: Oleksandr Zaitsev Date: Sat, 23 Apr 2022 16:11:17 +0200 Subject: [PATCH 4/6] Changed the protocols --- src/Math-Tests-Complex/PMComplexTest.class.st | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Math-Tests-Complex/PMComplexTest.class.st b/src/Math-Tests-Complex/PMComplexTest.class.st index 10484ade..2e24c0ea 100644 --- a/src/Math-Tests-Complex/PMComplexTest.class.st +++ b/src/Math-Tests-Complex/PMComplexTest.class.st @@ -178,7 +178,7 @@ PMComplexTest >> testBug1 [ self assert: (0.5 * (2 + 0 i) ln) exp equals: (0.5 * 2 ln) exp ] -{ #category : #tests } +{ #category : #'testing - close to' } PMComplexTest >> testCloseTo [ self assert: 2 + 3.000000000000001i closeTo: 2 + 3i. @@ -186,21 +186,21 @@ PMComplexTest >> testCloseTo [ self assert: 2.000000000000001 + 3.000000000000001i closeTo: 2 + 3i. ] -{ #category : #tests } +{ #category : #'testing - close to' } PMComplexTest >> testCloseToReal [ self assert: 2 + 0.000000000000001i closeTo: 2. self assert: 2.000000000000001 + 0.000000000000001i closeTo: 2. ] -{ #category : #tests } +{ #category : #'testing - close to' } PMComplexTest >> testCloseToRealWithPrecision [ self assert: (2 + 0.001i closeTo: 2 precision: 0.01). self assert: (2.001 + 0.001i closeTo: 2 precision: 0.01). ] -{ #category : #tests } +{ #category : #'testing - close to' } PMComplexTest >> testCloseToWithPrecision [ self assert: (2 + 3.001i closeTo: 2 + 3i precision: 0.01). @@ -541,14 +541,14 @@ PMComplexTest >> testNew [ self assert: c imaginary equals: 0 ] -{ #category : #tests } +{ #category : #'testing - close to' } PMComplexTest >> testNotCloseToRealWithPrecision [ self deny: (2 + 0.001i closeTo: 2 precision: 0.000001). self deny: (2.001 + 0.001i closeTo: 2 precision: 0.000001). ] -{ #category : #tests } +{ #category : #'testing - close to' } PMComplexTest >> testNotCloseToWithPrecision [ self deny: (2 + 3.001i closeTo: 2 + 3i precision: 0.000001). From c4ea1f17ab5bd9190fa7b762f1b04536be000dd2 Mon Sep 17 00:00:00 2001 From: Oleksandr Zaitsev Date: Sat, 23 Apr 2022 16:12:27 +0200 Subject: [PATCH 5/6] Removed the parentheses --- src/Math-Tests-Complex/PMComplexTest.class.st | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Math-Tests-Complex/PMComplexTest.class.st b/src/Math-Tests-Complex/PMComplexTest.class.st index 2e24c0ea..cc86615e 100644 --- a/src/Math-Tests-Complex/PMComplexTest.class.st +++ b/src/Math-Tests-Complex/PMComplexTest.class.st @@ -196,16 +196,16 @@ PMComplexTest >> testCloseToReal [ { #category : #'testing - close to' } PMComplexTest >> testCloseToRealWithPrecision [ - self assert: (2 + 0.001i closeTo: 2 precision: 0.01). - self assert: (2.001 + 0.001i closeTo: 2 precision: 0.01). + self assert: 2 + 0.001i closeTo: 2 precision: 0.01. + self assert: 2.001 + 0.001i closeTo: 2 precision: 0.01. ] { #category : #'testing - close to' } PMComplexTest >> testCloseToWithPrecision [ - self assert: (2 + 3.001i closeTo: 2 + 3i precision: 0.01). - self assert: (2.001 + 3i closeTo: 2 + 3i precision: 0.01). - self assert: (2.001 + 3.001i closeTo: 2 + 3i precision: 0.01). + self assert: 2 + 3.001i closeTo: 2 + 3i precision: 0.01. + self assert: 2.001 + 3i closeTo: 2 + 3i precision: 0.01. + self assert: 2.001 + 3.001i closeTo: 2 + 3i precision: 0.01. ] { #category : #tests } @@ -544,16 +544,16 @@ PMComplexTest >> testNew [ { #category : #'testing - close to' } PMComplexTest >> testNotCloseToRealWithPrecision [ - self deny: (2 + 0.001i closeTo: 2 precision: 0.000001). - self deny: (2.001 + 0.001i closeTo: 2 precision: 0.000001). + self deny: 2 + 0.001i closeTo: 2 precision: 0.000001. + self deny: 2.001 + 0.001i closeTo: 2 precision: 0.000001. ] { #category : #'testing - close to' } PMComplexTest >> testNotCloseToWithPrecision [ - self deny: (2 + 3.001i closeTo: 2 + 3i precision: 0.000001). - self deny: (2.001 + 3i closeTo: 2 + 3i precision: 0.000001). - self deny: (2.001 + 3.001i closeTo: 2 + 3i precision: 0.000001). + self deny: 2 + 3.001i closeTo: 2 + 3i precision: 0.000001. + self deny: 2.001 + 3i closeTo: 2 + 3i precision: 0.000001. + self deny: 2.001 + 3.001i closeTo: 2 + 3i precision: 0.000001. ] { #category : #tests } From 45bbc54ca2230a17b690f8b95011a046b72dac9c Mon Sep 17 00:00:00 2001 From: Oleksandr Zaitsev Date: Sat, 23 Apr 2022 16:15:47 +0200 Subject: [PATCH 6/6] Added two more assertions --- src/Math-Tests-Complex/PMComplexTest.class.st | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Math-Tests-Complex/PMComplexTest.class.st b/src/Math-Tests-Complex/PMComplexTest.class.st index cc86615e..2716e707 100644 --- a/src/Math-Tests-Complex/PMComplexTest.class.st +++ b/src/Math-Tests-Complex/PMComplexTest.class.st @@ -191,6 +191,9 @@ PMComplexTest >> testCloseToReal [ self assert: 2 + 0.000000000000001i closeTo: 2. self assert: 2.000000000000001 + 0.000000000000001i closeTo: 2. + + self assert: 2 + 0i closeTo: 2.000000000000001. + self deny: 2 + 3i closeTo: 2.000000000000001 ] { #category : #'testing - close to' }