RegressionTests__LargeFloatTest.st
author Claus Gittinger <cg@exept.de>
Tue, 28 May 2019 08:32:36 +0200
changeset 2248 55f846f2839b
parent 1828 44017f139f51
child 2249 098f9577859b
permissions -rw-r--r--
#QUALITY by cg class: RegressionTests::LargeFloatTest class definition added:82 methods class: RegressionTests::LargeFloatTest class added: #documentation #version #version_CVS

"{ Encoding: utf8 }"

"{ NameSpace: RegressionTests }"

TestCase subclass:#LargeFloatTest
	instanceVariableNames:'zero one two half minusOne minusTwo huge'
	classVariableNames:''
	poolDictionaries:''
	category:'tests-Regression-Numbers'
!

LargeFloatTest comment:'Test to check FloatingPoint numbers with arbitrary precision'
!

!LargeFloatTest class methodsFor:'documentation'!

documentation
"
    documentation to be added.

    [author:]
        Claus Gittinger

    [instance variables:]

    [class variables:]

    [see also:]

"
! !


!LargeFloatTest methodsFor:'private'!

checkDoublePrecision: y forFunction: func precision: n
	"Check that doubling the precision, then rounding would lead to the same result"
	
	| anLargeFloat singlePrecisionResult |
	anLargeFloat _ y asLargeFloatPrecision: n.
	singlePrecisionResult _ anLargeFloat perform: func.
	self checkThatEvaluatingFunction: func toDoublePrecisionOf: anLargeFloat equals: singlePrecisionResult.
	^singlePrecisionResult
!

checkDoublePrecisionSerie: serie forFunction: func 
	^self checkDoublePrecisionSerie: serie forFunction: func precision: Float precision
!

checkDoublePrecisionSerie: serie forFunction: func precision: n
	serie do: [:y | self checkDoublePrecision: y forFunction: func precision: n]
!

checkDoublePrecisionSerieVsFloat: serie forFunction: func 
	^serie reject: [:y |
		| farb |
		farb _ self checkDoublePrecision: y forFunction: func precision: Float precision.
		[(y asFloat perform: func) = farb] on: ZeroDivide do: [false]]
!

checkThatEvaluatingFunction: func toDoublePrecisionOf: anLargeFloat equals: singlePrecisionResult
	"Check that doubling the precision, then rounding would lead to the same result"
	
	| n doublePrecision doublePrecisionResult lowBits |
	n _ anLargeFloat precision.
	doublePrecision _ anLargeFloat asLargeFloatPrecision: n * 2.
	doublePrecisionResult _ doublePrecision perform: func.
	
	"Note: the test must be guarded against double rounding error condition.
	For example, suppose the single precision is 4 bits, double precision 8 bits.
	If exact result is 1.001 | 0111 | 1001...
	Then the nearest double is rounded to upper 1.001 | 1000
	Then the nearest single to the double is rounded to upper 1.010
	But the nearest single to the exact result should have been 1.001
	To avoid this, we have to check if the second rounding is an exact tie"
	doublePrecisionResult normalize.
	lowBits _ doublePrecisionResult mantissa bitAnd: 1<<n-1.
	lowBits = (1<<(n-1))
		ifTrue:
			["double precision is ambiguous - retry with quadruple..."
			^self checkThatEvaluatingFunction: func toQuadruplePrecisionOf: anLargeFloat equals: singlePrecisionResult].
	self assert: ((doublePrecisionResult asLargeFloatPrecision: n)- singlePrecisionResult) isZero
	
!

checkThatEvaluatingFunction: func toQuadruplePrecisionOf: anLargeFloat equals: singlePrecisionResult
	"Check that quadrupling the precision, then rounding would lead to the same result"
	
	| n quadruplePrecision quadruplePrecisionResult lowBits |
	n _ anLargeFloat precision.
	quadruplePrecision _ anLargeFloat asLargeFloatPrecision: n * 4.
	quadruplePrecisionResult _ quadruplePrecision perform: func.
	
	"Guard against double rounding error condition (exact tie)"
	quadruplePrecisionResult normalize.
	lowBits _ quadruplePrecisionResult mantissa bitAnd: 1<<(3*n)-1.
	lowBits = (1<<(3*n-1))
		ifTrue:
			["quadruple precision is ambiguous - give up..."
			^self].
	self assert: ((quadruplePrecisionResult asLargeFloatPrecision: n)- singlePrecisionResult) isZero.
! !

!LargeFloatTest methodsFor:'setup'!

setUp
        zero := 0 asLargeFloatPrecision: 53.
        one := 1 asLargeFloatPrecision: 53.
        two := 2 asLargeFloatPrecision: 53.
        half := (1/2) asLargeFloatPrecision: 53.
        minusOne := -1 asLargeFloatPrecision: 53.
        minusTwo := -2 asLargeFloatPrecision: 53.
        huge := (10 raisedTo: 100) asLargeFloatPrecision: 53.

    "Modified (format): / 27-05-2019 / 08:25:42 / Claus Gittinger"
! !

!LargeFloatTest methodsFor:'testing-arithmetic'!

testDivide
        | serie |
        serie := {1. 2. 3. 5. 6. 7. 9. 10. 11. 12. 19. 243. 
                 10 raisedTo: Float precision + 1. 
                 Float precision factorial. 
                 Float pi.
                }.
        serie do: [:num |
                | nf na |
                nf := num asFloat.
                na := num asLargeFloatPrecision: Float precision.
                serie do:[:den |
                        | df da ff fa |
                        df := den asFloat.
                        da := den asLargeFloatPrecision: Float precision.
                        ff := nf / df.
                        fa := na / da.
                        self assert: ff = fa]].

    "Modified (format): / 27-05-2019 / 10:13:11 / Claus Gittinger"
!

testIEEEArithmeticVersusFloat
	| floats ops ref new |
	floats _ #(1.0 2.0 3.0 5.0 10.0 2r1.0e52 2r1.0e53 2r1.0e54 0.5 0.25 2r1.0e-52 2r1.0e-53 2r1.0e-54 1.0e60 0.1 1.1e-30 1.0e-60) copyWith: Float pi.
	ops _ #(#+ #- #* #/ #= #< #> ).
	ops
		do: [:op | floats
				do: [:f1 | floats
						do: [:f2 | 
							ref _ f1 perform: op with: f2.
							new _ (f1 asLargeFloatPrecision: 53)
										perform: op
										with: (f2 asLargeFloatPrecision: 53).
							self assert: new = ref]]]
!

testIEEEArithmeticVersusIntegerAndFraction
	"check that results are the same as IEEE 754 accelerated hardware
	WARNING: this cannot be the case for denormalized numbers (gradual underflow)
	because our exponent is unlimited"

	| floats ops ref new intAndFractions |
	floats _ #(1.0e0 2.0e0 3.0e0 5.0e0 10.0e0) 
				, (#(52 53 54 -52 -53 -54) collect: [:e | 1.0e0 timesTwoPower: e]) 
					, #(0.5e0 0.25e0 1.0e60 0.1e0 1.1e-30 1.0e-60) copyWith: Float pi.
	intAndFractions _ #(1 3 5 10 12345678901234567890 -1 -22 -3) copyWith: 7/9.
	intAndFractions _ intAndFractions , (intAndFractions collect: [:e | e reciprocal]).
	
	ops _ 1/10 = 0.1
		ifTrue: [#(#+ #- #* #/)]
		ifFalse: [#(#+ #- #* #/ #= #< #>)]. "BEWARE: LargeFloat compare exactly, Float don't, unless http://bugs.squeak.org/view.php?id=3374"
	ops do: 
			[:op | 
			floats do: 
					[:f1 | 
					intAndFractions do: 
							[:f2 | 
							ref _ f1 perform: op with: f2 asFloat.
							new _ (f1 asLargeFloatPrecision: 53) perform: op
										with: (f2 asLargeFloatPrecision: 53).
							self assert: new = ref.
							new _ f1 perform: op
										with: (f2 asLargeFloatPrecision: 53).
							self assert: new = ref.
							
							ref _ f1 perform: op with: f2.
							new _ (f1 asLargeFloatPrecision: 53) perform: op
										with: f2.
							self assert: new = ref.
							
							ref _ f2 asFloat perform: op with: f1.
							new _ (f2 asLargeFloatPrecision: 53) perform: op
										with: (f1 asLargeFloatPrecision: 53).
							self assert: new = ref.
							new _ (f2 asLargeFloatPrecision: 53) perform: op with: f1.
							self assert: new = ref.
							
							ref _ f2 perform: op with: f1.
							new _ f2 perform: op
										with: (f1 asLargeFloatPrecision: 53).
							self assert: new = ref]]]
!

testMultiply
	self assert: zero * zero = zero.
	self assert: zero * minusOne = zero.
	self assert: huge * zero = zero.
	self assert: one * zero = zero.
	
	self assert: one * two = two.
	self assert: minusOne * one = minusOne.
	self assert: minusOne * minusTwo = two.
	
	self assert: half * two = one.
	
	"check rounding"
	self assert: huge * one = huge.
!

testNegated
	self assert: zero negated = zero.
	self assert: one negated = minusOne.
	self assert: minusTwo negated = two.
	self assert: huge negated negated = huge.
!

testPi
	"check computation of pi"

	self assert: (1 asLargeFloatPrecision: 53) pi = Float pi.
!

testRaisedToNegativeInteger
	| n |
	n _ 11.
	1 to: 1<<n-1 do: [:i |
		self assert: ((i asLargeFloatPrecision: n) raisedToInteger: -49)
			equals: ((i raisedToInteger: -49) asLargeFloatPrecision: n) ].
!

testRaisedToPositiveInteger
	| n |
	n _ 11.
	1 to: 1<<n-1 do: [:i |
		self assert: ((i asLargeFloatPrecision: n) raisedToInteger: 49)
			equals: ((i raisedToInteger: 49) asLargeFloatPrecision: n) ].
!

testReciprocal
	| b |
	b _ 1 << (Float precision - 1).
	1 to: 10000 do: [:i |
		| a |
		a _ i asLargeFloatPrecision: Float precision.
		self assert: a reciprocal = i asFloat reciprocal.
		self assert: (a+b) reciprocal = (i+b) asFloat reciprocal.
		self assert: a negated reciprocal = i asFloat negated reciprocal.]
!

testRoundToNearestEven
	"Check that IEEE default rounding mode is honoured,
	that is rounding to nearest even"
		
	self assert: ((one timesTwoPower: 52)+(0+(1/4))) asTrueFraction = ((1 bitShift: 52)+0).
	self assert: ((one timesTwoPower: 52)+(0+(1/2))) asTrueFraction = ((1 bitShift: 52)+0).
	self assert: ((one timesTwoPower: 52)+(0+(3/4))) asTrueFraction = ((1 bitShift: 52)+1).
	self assert: ((one timesTwoPower: 52)+(1+(1/4))) asTrueFraction = ((1 bitShift: 52)+1).
	self assert: ((one timesTwoPower: 52)+(1+(1/2))) asTrueFraction = ((1 bitShift: 52)+2).
	self assert: ((one timesTwoPower: 52)+(1+(3/4))) asTrueFraction = ((1 bitShift: 52)+2).
!

testRoundToNearestEvenAgainstIEEEDouble
	"Check that IEEE default rounding mode is honoured"

	#(1 2 3 5 6 7) do: 
			[:i | 
			self assert: ((one timesTwoPower: 52) + (i / 4)) asTrueFraction 
						= ((1 asFloat timesTwoPower: 52) + (i / 4)) asTrueFraction.
			self assert: ((one timesTwoPower: 52) - (i / 4)) asTrueFraction 
						= ((1 asFloat timesTwoPower: 52) - (i / 4)) asTrueFraction]
!

testSubtract
	self assert: zero - zero = zero.
	self assert: zero - minusOne = one.
	self assert: huge - zero = huge.
	self assert: one - zero = one.
	
	self assert: one - minusOne = two.
	self assert: minusOne - minusTwo = one.
	self assert: minusOne - one = minusTwo.
	
	"check rounding"
	self assert: huge - one = huge.
!

testSum
	self assert: zero + zero = zero.
	self assert: zero + minusOne = minusOne.
	self assert: huge + zero = huge.
	self assert: one + zero = one.
	
	self assert: one + minusOne = zero.
	self assert: minusOne + two = one.
	self assert: one + minusTwo = minusOne.
	
	"check rounding"
	self assert: huge + one = huge.
!

testZeroOne

	self assert: (312 asLargeFloatPrecision: 53) one = 1.
	self assert: (312 asLargeFloatPrecision: 24) zero isZero.

	self assert: (312 asLargeFloatPrecision: 53) one asInteger = 1.
	self assert: (312 asLargeFloatPrecision: 24) zero asInteger isZero.
! !

!LargeFloatTest methodsFor:'testing-coercing'!

testCoercingDivide
	(Array with: 1/2 with: 0.5e0) do: [:heteroHalf |
		self assert: one / heteroHalf = two.
		self assert: (one / heteroHalf) class = one class.
		self assert: (one / heteroHalf) precision = one precision.
		self assert: heteroHalf / one = half.
		self assert: (heteroHalf / one) class = one class.
		self assert: (heteroHalf / one) precision = one precision].

	self assert: one / 2 = half.
	self assert: (one / 2) class = one class.
	self assert: (one / 2) precision = one precision.
	self assert: -2 / two = minusOne.
	self assert: (-2 / two) class = two class.
	self assert: (-2 / two) precision = two precision.
!

testCoercingEqual
	self assert: half = (1/2).
	self assert: (1/2) = half.
	self deny: half = (1/3).
	self deny: (1/3) = half.

	self assert: two = 2.
	self assert: -2 = minusTwo.
	self deny: -3 = two.
	self deny: two = 3.

	self assert: half = (0.5e0).
	self assert: (0.5e0) = half.
	self deny: half = (0.33e0).
	self deny: (0.33e0) = half.
!

testCoercingLessThan
	self deny: half < (1/2).
	self assert: (1/3) < half.
	self assert: minusOne < (1/2).
	self deny: (1/3) < minusTwo.

	self assert: two < 3.
	self deny: two < 2.
	self deny: two < 1.
	self deny: two < -1.
	self assert:  minusTwo < -1.
	self assert:  minusTwo < 1.
	self deny: minusTwo < -2.
	self deny: minusTwo < -3.

	self deny: half < (0.5e0).
	self deny: half < (0.33e0).
	self assert: half < (0.66e0).
	self deny: (0.5e0) < half.
	self assert: (0.33e0) < half.
	self deny: (0.66e0) < half.
!

testCoercingMultiply
	(Array with: 1/2 with: 0.5e0) do: [:heteroHalf |
		self assert: two * heteroHalf = one.
		self assert: (two * heteroHalf) class = half class.
		self assert: (two * heteroHalf) precision = half precision.
		self assert: heteroHalf * two = one.
		self assert: (heteroHalf * two) class = half class.
		self assert: (heteroHalf * two) precision = half precision].

	self assert: minusOne * 2 = minusTwo.
	self assert: (minusOne * 2) class = minusOne class.
	self assert: (minusOne * 2) precision = minusOne precision.
	self assert: 2 * one = two.
	self assert: (2 * one) class = one class.
	self assert: (2 * one) precision = one precision.
!

testCoercingSubtract
	(Array with: 1/2 with: 0.5e0) do: [:heteroHalf |
		self assert: half - heteroHalf = zero.
		self assert: (half - heteroHalf) class = half class.
		self assert: (half - heteroHalf) precision = half precision.
		self assert: heteroHalf - half = zero.
		self assert: (heteroHalf - half) class = half class.
		self assert: (heteroHalf - half) precision = half precision].

	self assert: one - 1 = zero.
	self assert: (one - 1) class = minusOne class.
	self assert: (one - 1) precision = minusOne precision.
	self assert: -2 - minusTwo = zero.
	self assert: (-2 - minusTwo) class = minusTwo class.
	self assert: (-2 - minusTwo) precision = minusTwo precision.
!

testCoercingSum
	(Array with: 1/2 with: 0.5e0) do: [:heteroHalf |
		self assert: half + heteroHalf = one.
		self assert: (half + heteroHalf) class = half class.
		self assert: (half + heteroHalf) precision = half precision.
		self assert: heteroHalf + half = one.
		self assert: (heteroHalf + half) class = half class.
		self assert: (heteroHalf + half) precision = half precision].

	self assert: minusOne + 1 = zero.
	self assert: (minusOne + 1) class = minusOne class.
	self assert: (minusOne + 1) precision = minusOne precision.
	self assert: 2 + minusTwo = zero.
	self assert: (2 + minusTwo) class = minusTwo class.
	self assert: (2 + minusTwo) precision = minusTwo precision.
!

testInfinityAndNaN
	| inf nan |
	inf _ Float infinity.
	nan _ Float nan.
	self assert: inf + two equals: inf.
	self assert: half + inf negated equals: inf negated.	
	self assert: (nan + minusOne)  isNaN .
	self assert: inf - huge equals: inf.
	self assert: half - inf equals: inf negated.
	self assert: minusTwo - inf negated equals: inf.
	self assert: (one - nan) isNaN.
	self assert: inf * two equals: inf.
	self assert: minusOne * inf equals: inf negated.
	self assert: inf negated * minusOne equals: inf.
	self assert: (huge * nan) isNaN.
	self assert: inf negated / minusTwo equals: inf.
	self assert: zero / inf negated equals: 0.	
	self assert: one / inf equals: 0.
	self should: [inf / zero] raise: ZeroDivide.	
	self assert: (nan  / two) isNaN.	
	self assert: (inf raisedTo: huge) equals: inf.
	self assert: (huge raisedTo: inf) equals: inf.
	self assert: (nan raisedTo: two) isNaN.
	self assert: (two raisedTo: nan) isNaN.
	self deny: nan <= one.
	self deny: zero >= nan.
	self assert: one < inf.
	self assert: zero ~= nan.
	self deny: nan = one.
! !

!LargeFloatTest methodsFor:'testing-compare'!

testEqual
	self assert: zero = zero.
	self assert: one = one.
	self assert: one = one copy.
	self assert: one = (one asLargeFloatPrecision: one precision * 2).

	self deny: zero = one.
	self deny: minusOne = one.

	self assert: zero = 0.
	self assert: 0 = zero.
	self assert: zero = 0.0.
	self assert: 0.0 = zero.

	self deny: two = (1/2).
	self deny: (1/2) = two.
	self deny: zero = 1.0.
	self deny: 0.0 = one.

	self deny: one = nil.
	self deny: nil = one.
!

testGreaterThan
	
	self assert: zero < one.
	self deny: one > two.
	self deny: two > huge.
	self deny: minusOne > one.
	self deny: minusTwo > minusOne.
	self deny: minusTwo > huge.
	
	self assert: huge > one.
	self assert: huge > zero.
	self assert: huge > minusOne.
	self assert: one > minusOne.
	self assert: minusOne > minusTwo.
!

testIsZero
	self assert: zero isZero.
	self deny: one isZero.
	self deny: minusTwo isZero.
!

testLessThan
	
	self assert: zero < one.
	self assert: one < two.
	self assert: two < huge.
	self assert: minusOne < one.
	self assert: minusTwo < minusOne.
	self assert: minusTwo < huge.
	
	self deny: huge < one.
	self deny: huge < zero.
	self deny: huge < minusOne.
	self deny: one < minusOne.
	self deny: minusOne < minusTwo.
!

testNegative
	
	self deny: zero negative.
	self deny: two negative.
	self assert: minusTwo negative.
!

testPositive
	
	self assert: zero positive.
	self assert: one positive.
	self deny: minusOne positive.
! !

!LargeFloatTest methodsFor:'testing-converting'!

testAsFloat
	self assert: (half asLargeFloatPrecision: Float precision) asFloat = 0.5e0.
	self assert: (half asLargeFloatPrecision: Float precision * 2) asFloat = 0.5e0.
!

testAsFloatWithUnderflow
	| fmin fminA |
	fmin _ Float fmin.
	fminA _ fmin asLargeFloatPrecision: one precision.
	Float emin - Float precision + 1 to: Float emin + 1 do: [:n |
		self assert: ((one timesTwoPower: n) + fminA) asFloat = ((1.0e0 timesTwoPower: n) + fmin)].
!

testAsMinimalDecimalFraction
	| emax emin leadingOne significands |
	significands _ 0 to: 1<<10-1.
	leadingOne _ 1<<10.
	emin _ -14.
	emax _ 15.
	
	"Test all normal finite half precision float"
	emin to: emax do: [:e | 
		significands do: [:s |
			| f |
			f _ (leadingOne + s asLargeFloatPrecision: 11) timesTwoPower: e - 10.
			self assert: (f asMinimalDecimalFraction asLargeFloatPrecision: 11) = f]].
	
	"Test all subnormal finite half precision float"
	significands do: [:s |
		| f |
		f _ (s asLargeFloatPrecision: s highBit) timesTwoPower: emin - 10.
		self assert: (f asMinimalDecimalFraction asLargeFloatPrecision: s highBit) = f].
!

testPrintAndEvaluate
	<timeout: 50 "seconds">
	| emax emin leadingOne significands |
	significands _ 0 to: 1<<10-1.
	leadingOne _ 1<<10.
	emin _ -14.
	emax _ 15.
	
	"Test all normal finite half precision float"
	emin to: emax do: [:e | 
		significands do: [:s |
			| f |
			f _ (leadingOne + s asLargeFloatPrecision: 11) timesTwoPower: e - 10.
			self assert: (Compiler evaluate: f storeString) = f.
			self assert: (Compiler evaluate: f printString) = f.]].
	
	"Test all subnormal finite half precision float"
	significands do: [:s |
		| f |
		f _ (s asLargeFloatPrecision: s highBit) timesTwoPower: emin - 10.
		self assert: (Compiler evaluate: f storeString) = f.
		self assert: (Compiler evaluate: f printString) = f].
! !

!LargeFloatTest methodsFor:'testing-functions'!

testExp
	<timeout: 10 "seconds">
	| badExp serie |
	serie _ ((-20 to: 20) collect: [:e |e asFloat]).
	badExp _ self checkDoublePrecisionSerieVsFloat: serie forFunction: #exp.
	badExp isEmpty ifFalse: [Transcript cr; show: 'bad exp for ' , badExp printString]
!

testExpLn
	|n|
	self assert: (1 asLargeFloatPrecision: 53) exp asFloat = 1 asFloat exp.
	n _ 5 exp.
	self assert: ((5 asLargeFloatPrecision: 53) exp - n)abs <= n ulp.
	"self assert: (5 asLargeFloatPrecision: 53) exp asFloat = 5 asFloat exp."
	self assert: ((5 asLargeFloatPrecision: 53) exp ln asFloat - n ln)abs <= 5.0 ulp.
	"this test was skipped. changed that & loosened 2. test,
	 since '5 exp' seems to round up instead of down here,
	 which results in an error of almost one ulp in '5 exp'"
!

testLn
	<timeout: 10 "seconds">
	| badLn serie |
	serie _ ((1 to: 100) collect: [:e |e asFloat]).
	badLn _ self checkDoublePrecisionSerieVsFloat: serie forFunction: #ln.
	badLn isEmpty ifFalse: [Transcript cr; show: 'bad ln for ' , badLn printString]
!

testLnDomainError
	self should: [(-2 asLargeFloatPrecision: 24) ln] raise: DomainError.
!

testSqrt
	<timeout: 10 "seconds">
	| badSqrt serie |
	"knowing that (10**3) < (2**10), 100 bits are enough for representing 10**30 exactly"
	self assert: ((10 raisedTo: 30) asLargeFloatPrecision: 100) sqrt = (10 raisedTo: 15).

	serie _ ((0 to: 20) collect: [:e | e asFloat]) , ((2 to: 20) collect: [:e | e reciprocal asFloat]).
	badSqrt _ self checkDoublePrecisionSerieVsFloat: serie forFunction: #sqrt.
	badSqrt isEmpty ifFalse: [Transcript cr; show: 'bad sqrt for ' , badSqrt printString]
!

testSqrtDomainError
	self should: [(-2 asLargeFloatPrecision: 24) sqrt] raise: DomainError.
! !

!LargeFloatTest methodsFor:'testing-hyperbolic'!

hyperbolicSerie
	^#(-3.0e0  -0.1e0  0.0e0  1.0e-20  1.0e-10  0.99e0 1.0e0  2.5e0  3.0e0  10.25e0) , (Array with: (3/10) asFloat with: (22/7) asFloat)
!

testArCosh
	<timeout: 5 "seconds">
	| serie |
	serie _ ((1 to: 10) , #(1.0001 100 1000 1.0e20)) collect: [:e | e asFloat].
	self checkDoublePrecisionSerie: serie forFunction: #arCosh
!

testArCoshDomainError
	self should: [(1/2 asLargeFloatPrecision: 24) arCosh] raise: DomainError.
!

testArSinh
	<timeout: 10 "seconds">
	| serie |
	serie _ ((-5 to: 10) , #(1.0e-20 1.0e-10  0.9999 1.0001 100 1000 1.0e20)) collect: [:e | e asFloat].
	self checkDoublePrecisionSerie: serie forFunction: #arSinh
!

testArTanh
	<timeout: 20 "seconds">
	| serie |
	serie _ ((-19 to: 19) collect: [:e | (e / 20) asFloat]) , ((-6 to: 6) collect: [:e | (e / 7) asFloat]) , #(1.0e-20 1.0e-10 0.99 0.9999 0.999999).
	self checkDoublePrecisionSerie: serie forFunction: #arTanh
!

testArTanhDomainError
	self should: [(2 asLargeFloatPrecision: 24) arTanh] raise: DomainError.
	self should: [(-3 asLargeFloatPrecision: 24) arTanh] raise: DomainError.
!

testCosh
	<timeout: 10 "seconds">
	self checkDoublePrecisionSerie: self hyperbolicSerie forFunction: #cosh
!

testSinh
	<timeout: 10 "seconds">
	self checkDoublePrecisionSerie: self hyperbolicSerie forFunction: #sinh
!

testTanh
	<timeout: 10 "seconds">
	self checkDoublePrecisionSerie: self hyperbolicSerie forFunction: #tanh
! !

!LargeFloatTest methodsFor:'testing-trigonometry'!

inverseTrigonometricSerie
	^((-20 to: 20) collect: [:e | (e / 20) asFloat]) , ((-6 to: 6) collect: [:e | (e / 7) asFloat])
!

largeTrigonometricSerie
	^#(1.0e15 1.1e21 1.2e28 1.0e32 1.1e34 -1.23e51 1.345e67 1.777e151 1.211e308)
!

testArcCos
	<timeout: 10 "seconds">
	| badArcCos |
	badArcCos _ self checkDoublePrecisionSerieVsFloat: self inverseTrigonometricSerie forFunction: #arcCos.
	badArcCos isEmpty ifFalse: [Transcript cr; show: 'bad arcCos for ' , badArcCos printString]
!

testArcCosDomainError
	self should: [(2 asLargeFloatPrecision: 24) arcCos] raise: DomainError.
	self should: [(-3 asLargeFloatPrecision: 24) arcCos] raise: DomainError.
!

testArcSin
	<timeout: 10 "seconds">
	| badArcSin |
	badArcSin _ self checkDoublePrecisionSerieVsFloat: self inverseTrigonometricSerie forFunction: #arcSin.
	badArcSin isEmpty ifFalse: [Transcript cr; show: 'bad arcSin for ' , badArcSin printString]
!

testArcSinDomainError
	self should: [(2 asLargeFloatPrecision: 24) arcSin] raise: DomainError.
	self should: [(-3 asLargeFloatPrecision: 24) arcSin] raise: DomainError.
!

testArcTan
	<timeout: 10 "seconds">
	| badArcTan serie |
	serie _ ((-50 to: 50) collect: [:e | (e / 10) asFloat]).
	badArcTan _ self checkDoublePrecisionSerieVsFloat: serie forFunction: #arcTan.
	badArcTan isEmpty ifFalse: [Transcript cr; show: 'bad arcTan for ' , badArcTan printString]
!

testArcTan2
	<timeout: 30 "seconds">
	-5 to: 5 by: 4/10 do: [:y |
		| yf yd |
		yf _ y asLargeFloatPrecision: Float precision.
		yd _ yf asLargeFloatPrecision: Float precision * 2.
		-5 to: 5 by: 4/10 do: [:x |
			| xf xd  |
			xf _ x asLargeFloatPrecision: Float precision.
			xd _ xf asLargeFloatPrecision: Float precision * 2.
			self assert: ((yd arcTan: xd) asFloat - (yf arcTan: xf) asFloat) isZero]].
!

testCos
	<timeout: 30 "seconds">
	| badCos |
	badCos _ self checkDoublePrecisionSerieVsFloat: self trigonometricSerie forFunction: #cos.
	badCos isEmpty ifFalse: [Transcript cr; show: 'bad cos for angles (degrees) ' , (badCos collect: [:i | i radiansToDegrees rounded]) printString]
!

testSin
	<timeout: 30 "seconds">
	| badSin |
	badSin _ self checkDoublePrecisionSerieVsFloat: self trigonometricSerie forFunction: #sin.
	badSin isEmpty ifFalse: [Transcript cr; show: 'bad sin for angles (degrees) ' , (badSin collect: [:i | i radiansToDegrees rounded]) printString]
!

testSincos
	<timeout: 30 "seconds">
	self trigonometricSerie do: [:aFloat |
		| x sc s c |
		x _ aFloat asLargeFloatPrecision: 53.
		sc _ x sincos.
		s _ x sin.
		c _ x cos.
		self assert: sc size = 2.

		self assert: sc first = s.
		self assert: sc last = c]
!

testTan
	<timeout: 30 "seconds">
	| badTan |
	badTan _ self checkDoublePrecisionSerieVsFloat: self trigonometricSerie forFunction: #tan.
	badTan isEmpty ifFalse: [Transcript cr; show: 'bad tan for angles (degrees) ' , (badTan collect: [:i | i radiansToDegrees rounded]) printString]
!

testVeryLargeCos
	<timeout: 10 "seconds">
	self checkDoublePrecisionSerie: self largeTrigonometricSerie forFunction: #cos.
!

testVeryLargeSin
	<timeout: 10 "seconds">
	self checkDoublePrecisionSerie: self largeTrigonometricSerie forFunction: #sin.
!

testVeryLargeTan
	<timeout: 10 "seconds">
	self checkDoublePrecisionSerie: self largeTrigonometricSerie forFunction: #tan.
!

trigonometricSerie
	^(-720 to: 720) collect: [:i | i asFloat degreesToRadians]
! !