gimp/plug-ins/script-fu/test/tests/TS/numeric.scm

272 lines
8.1 KiB
Scheme

; test numerics of TS
; TinyScheme supports the numeric tower "fixnums and flonums"
; Meaning the tower is number=>real=>integer, omitting actual rational and complex.
; Also meaning the machine representations as integer or float
; Note "machine representation" is not "string representation"
; Former is a machine implementation.
; Latter is how the interpreter prints numbers.
; These tests only show what TinyScheme does.
; These tests might not reflect what a R5RS conforming implementation should do.
; See comments.
; TinyScheme is technically exactness-preserving,
; but silently returns the wrong answers when arithmetic operations overflow.
; Thus TinyScheme is non-conformant.
; !!! This must be also be tested with non-English locale
; e.g. export LANG="de_DE.UTF-8"
; In that locale, comma is used as a decimal point.
; In TinyScheme, string representation of numbers SHOULD use period as decimal point.
; But currently doesn't, in some locales using comman for decimal point.
; Because TinyScheme uses C standard lib snprintf
; which respects locale and can print comma for decimal point.
; float precision
; R5RS allows 1.0[s,e,d,l]0 notation
; meaning short-float, single-float, double-float and long-float.
; TinyScheme only supports e
(test! "e notation is supported")
(assert `(= 1.0e0 1.0))
(assert `(= 1.0e+0 1.0))
(assert `(= 1.0e-0 1.0))
(test! "e[s,d,l] notation is treated as a symbol, and is unbound")
(assert-error `1.0s0
"eval: unbound variable")
(test! "max integers")
; largest 64-bit integer
; 1234567890123456789 digit counter
; unsigned 18446744073709551615
; signed 9223372036854775807
; TinyScheme machine representation is signed 64-bit.
;
; Any int literal greater than 19 digits overflows silently.
; Int literal of 19 digits < max signed 64-bit int do not overflow.
(test! "integers overflow silently.")
; Adding one to the largest representable int yields the wrong result
(assert `(not
(=
(+ 9223372036854775807 1)
9223372036854775808)))
; And is in fact a negative int (overflowing into the sign bit)
(assert `(=
(+ 9223372036854775807 1)
-9223372036854775808))
; Numbers larger than the largest representable int
; can be represented as float literals, but not exact.
(assert `(inexact? 9223372036854775808.0))
; and they are not integers
(assert `(not (integer? 9223372036854775808.0)))
; Such numbers can be represented in equal e notation
(assert `(=
9223372036854775808.0
9.223372036854775808e+18))
; The string representation loses significant digits
; FIXME arbitrary limiting string repr to 10 decimal places is wrong.
(assert `(string=? (number->string 9223372036854775808.0)
"9.223372037e+18"))
; "9.2233720368547758e+18" when fixed, having more digits but still losing digits.
(test! "numeric type predicates")
; These do NOT tell you the machine representation.
; "The behavior of these type predicates on inexact numbers is unreliable, since any inaccuracy may affect the result."
(assert `(integer? 3.0))
(assert `(integer? (/ 8 4))) ; i.e. 2.0
(assert `(not (integer? (/ 1 3)))) ; i.e. 0.3333
; integer implies rational implies real implies complex implies number
; integer is a subset of all higher types
(assert `(integer? 3))
(assert `(rational? 3))
(assert `(real? 3))
(assert `(complex? 3))
(assert `(number? 3))
(test! "complex type is not implemented")
(assert-error `(imag-part 3)
"eval: unbound variable")
; exactness predicates
; The usual predicates are defined.
; R5RS says: "One of the predicates must be true. Exactly one of the two can be true."
(assert `(exact? 1))
(assert `(not (inexact? 1)))
; exact-integer? exact-nonnegative-integer? exact-rational? are not defined
(assert-error `(exact-integer? 1)
"eval: unbound variable")
; exactness
(test! "exact")
; small integers are exact
(assert `(exact? 1))
; some float notation (with all fractional digits zero) is exact
(assert `(exact? 4.0))
(assert `(real? 4.0))
(assert `(integer? 4.0))
; float notation with fractional digits nonzero is not exact
(assert `(inexact? 1.1))
; exactness and IEEE representability
;
; Note: exactness is not the same concept as
; "representable in IEEE float without loss of precision"
; Some large numbers seemingly integral
; are not representable in IEEE float:
; some integers having greater than 15 digits.
; This is moot for TinyScheme since if they have no non-zero fractional digits,
; they are integer?
; integers greater than 19 digits are exact but have overflowed
(assert `(exact? 12345678901234567890))
; 3122.55 is not representable in IEEE float
; but is represented by the nearest representable value.
; Which is 3122.5500000000001818989403545856475830078125
; 17 significant digits: 3122.5500000000002, with >10 decimal places.
; TS uses %.10g printf format.
(assert `(inexact? 3122.55))
; The string representation is only 10 digits
; so rounded and truncated trailing zeros.
; FIXME this fails in non-English locale
; Currently prints as 3122,55.0
(assert `(string=? (number->string 3122.55)
"3122.55"))
; exactness and operations
; "inexactness is a contagious property of a number", with exceptions
; I.E. any operations with an inexact operand produces an inexact result,
; with exceptions.
; Division of any two exact numbers is exact
; This is only an illustration, not an exhaustive test
(assert `(exact?
(/ 4.0 2.0)))
; exception:
; multiplication of any number by an exact zero may produce an exact zero result, even if the other argument is inexact.
(assert `(exact?
(* 0 3122.55)))
; equivalence of exact and inexact numbers
; Scheme says: never equivalent.
; The literals look the same, and are exact, but converting one to inexact.
; FUTURE: this fails because exact->inexact fails to produce an inexact
; This is extremely obscure, so wait for upstream fix.
;; (assert `(not (eqv? 4.0 (exact->inexact 4.0))))
; inexact->exact
; Non-integral numbers cannot be converted to exact
(assert-error `(inexact->exact 3122.55)
"inexact->exact: not integral:")
; A large integral number (only representable in float machine representation)
; can be converted to exact.
(assert `(exact? (inexact->exact 9223372036854775808.0)))
; But the result is not correct.
; Here the result is the least signed integer representable in signed 64-bit
(assert `(= (inexact->exact 9223372036854775808.0)
-9223372036854775808))
; exact->inexact
; Is defined
(assert `exact->inexact)
; FUTURE fails, TinyScheme is non-compliant
;; (assert `(inexact? (exact->inexact 4.0)))
(test! "Round-trip (string->number (number->string x))")
; conversions to and from strings
;
; Meets a round-trip conversion predicate.
; A string representation of a number converts back to the same number.
(define (round-trip-repr? number)
(eqv? number
(string->number (number->string number))))
; Note, no backtick on calls to predicate
(assert (round-trip-repr? 1.0))
(assert (round-trip-repr? 1.01))
(assert (round-trip-repr? 3122.55))
; The string representation is fixed at 10 digits after decimal point.
; So numbers with more precision don't round-trip
(assert (not (round-trip-repr? 1.01234567890123)))
; FIXME The test is inverted. This number should round-trip
; Currently prints as 1.012345679, losing 4 significant digits.
; Note that IEEE double can represent about 15 decimal places
; but TinyScheme somewhat arbitrarily limits the string representation
; to 10 decimal places
; Test various literals
(test! "string->number")
(assert `(= (string->number "100")
100))
(assert `(= (string->number "1e2")
100.0))
; the notation using # for unspecified digit is not supported
; TinyScheme returns #f, some Schemes return 1500.0
(assert `(not (string->number "15##")))
(test! "radix arg to string->number")
; hexadecimal
(assert `(= (string->number "100" 16)
256))
; numeric literals
; In Scheme, the #e prefix makes a literal exact
; but TinyScheme returns a syntax error.
; FUTURE: the test framework can't test syntax errors
;;(assert-error `#e1
;; "syntax: illegal sharp expression")