All R6RS systems behave the same way: `(imag-part 3.0+0i)` => 0.0, whereas `(imag-part 3.0)` => 0. However, when we take into account the R5RS and R7RS Schemes that support both exact and inexact complex numbers, things become more complicated:Chicken 4 with the numbers egg, Chicken 5, and Scheme 48 return 0.0 as the value of both `(imag-part 3.0+0i)` and `(imag-part 3.0)`.Gambit, MIT, and STklos return 0 as the value of both `(imag-part 3.0+0i)` and `(imag-part 3.0)`.Lastly, in Chibi `(imag-part 3.0+0i) => 0 but `(imag-part 3.0) => 0.0, the exact opposite of R6RS.
How is "rational?" related to this?
Chibi gives (real? 3.0+0.0i) => #t but (rational? 3.0+0.0i) => #f.
What about adding an optional library "(scheme r6rs)" to R7RS-large?
Chibi behaves the same as Gambit, MIT and STklos, returning exact 0 for numbers with no imaginary part.
To view this discussion on the web visit https://groups.google.com/d/msgid/scheme-reports-wg2/214344a4-71cc-4411-a00b-3788125a8bc8n%40googlegroups.com.
You received this message because you are subscribed to a topic in the Google Groups "scheme-reports-wg2" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/scheme-reports-wg2/EAF0ZsFeqmU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to scheme-reports-...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/scheme-reports-wg2/CAEYrNrTUyqcUg0huKtQ_hC9T3tvkZOs%3D4jVhUu3iYTm9Q%3Dy2HQ%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/scheme-reports-wg2/CAEYrNrTUyqcUg0huKtQ_hC9T3tvkZOs%3D4jVhUu3iYTm9Q%3Dy2HQ%40mail.gmail.com.
--
You received this message because you are subscribed to a topic in the Google Groups "scheme-reports-wg2" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/scheme-reports-wg2/EAF0ZsFeqmU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to scheme-reports-...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/scheme-reports-wg2/78306483-DB18-46B3-B42C-EBC4BAD19B60%40mac.com.
Another angle to take is: What are people using numbers for in Scheme?
What are the use cases? What are their expectations (or, to avoid beuing
trapped by the common case of
programmers-not-really-understanding-floating-point-numbers) what SHOULD
their expectations be?
And yet sometimes that's not what we want. When writing code to emulate
hardware arithmetic circuits (eg, because we're emulating some VM, or an
encryption/hashing algorithm defined in terms of fixed-width registers),
I am not a numerical computing expert, so please correct me if I'm
wrong, but I think that float-of-unspecifed-precision is far too woolly
a notion for any serious work where correctness matters;
(As an interesting aside, I wasn't the original author of this code. I
went in to fix it because it wasn't working and nobody knew why. I found
that the code was written as a direct translation of a formula from a
paper, and had an expression something like pow(1.0-1.0/rows_in_table,
values_in_column) - where rows_in_table would be in the billions or
trillions, so 1.0/rows_in_table would be a tiny number, and 1.0 fp-minus
that would often just come out as 1.0, so the code *always produced the
same estimates*. On that day I found out why the C math.h library has a
log1p function that computes log(1+x) even when x is close to zero, so I
could rewrite it as exp(values_in_column*log1p(-1.0/rows_in_table)) and
suddenly our engine produced half-sensible join orders - as I mentioned
earlier, programmers are often confused by the nature of floating point
numbers)
(As a second aside, the original programmer had seemed unaware of
there should be fixed-width decimal types so people doing money can do
money right (floats really, really, aren't the way to do money right).
We can do symbolic numbers, so (sqrt 2) can be represented properly.
Complex numbers are more interesting, as they are usually represented as
two real parts. We could debate the mathematical meaning of a complex
number with an exact real part and an inexact imaginary part (or inexact
magnitude and exact angle), but I really don't know enough about the
practical uses of complex numbers in software to say anything useful
about "But what would actually be useful to programmers?".
Would "IEEE binary32 complex" be a useful type of number to somebody,
which in my world would be tainted by the IEEE-float-ness and so need to
be kept isolated from the numeric tower?
Would "complex of proper numbers (be they integers or rationals or
rational intervals or symbolic numbers)" be useful?
Complex numbers with non-floaty semantics in the numeric tower is
certainly doable, but what would they be useful for?
Are the applications of complex numbers in software more in the
numeric-programming domain that floats are made for, and appropriate
for, and the users can be expected to know about the caveats?
In other words, 1.0 should just be a verbose way of writing 1,
and 1.5
should be an exact rational.
(sqrt 2) and (sqrt -1) should raise an
error unless you have symbolic numbers,
while (ieee734:sqrt (ieee734:make-binary32 2)) should return ~1.4142135623731,
(ieee734:sqrt
(ieee734:make-binary32 -1)) should return +nan.0,
and
(ieee734-complex:sqrt (ieee734:make-binary32 -1)) should return
~0.0+~1.0i :-)
I know that this is not the motivation for R6RS's specification of the `real?' procedure, but In any case, I don't think that exact non-real complex numbers are very interesting. Complex numbers of mixed exactness even less (I would remove them if that makes things simpler).
On Thursday, February 21, 2019 at 11:46:34 AM UTC-5 Jim Rees wrote:I am confused about the errata at https://small.r7rs.org/wiki/R7RSSmallErrata/Item #24 regarding the example "(real? 2.5+0.0i)" says the conflict hasn't yet been resolved, but you say here and item #19 says that r7rs did not adopt r6rs semantics, which to me says (real? 2.5+0.0i) ==> #t as an R7RS requirement and there is no conflict.
I know that this is not the motivation for R6RS's specification of the `real?' procedure, but In any case, I don't think that exact non-real complex numbers are very interesting. Complex numbers of mixed exactness even less (I would remove them if that makes things simpler).
I hear talk of Gaussian integers from time to time,
One reason to want numbers-in-Scheme to be "like" the numbers described
in mathematics is to allow reasoning about them. C is infamous for its
arithmetic being full of surprises and undefined behaviour,
Making numbers-in-Scheme behave "like" "actual" numbers as we
learnt about in school reduces the chance for subtle bugs and annoying
problems
And that's just integers. What about floating point numbers? All our
talk of the real meaning of inexact numbers in Scheme, I feel, is
skirting around the fact that we're trying to give a sensible
mathematical semantics to things like IEEE floats (and their relatives,
such as bfloat16 - and maybe other less-used representations
(I vaguely
remember reading about a numeric representation that was a fixed-point
binary representation of the logarithm of the number, whatever happened
to that?)).
And
there should be fixed-width decimal types so people doing money can do
money right (floats really, really, aren't the way to do money right).
Are the applications of complex numbers in software more in the
numeric-programming domain that floats are made for, and appropriate
for, and the users can be expected to know about the caveats?
All that I want out of scheme is a conventional, "common-sense" kind of double-precision math library, with any strange warts and blebs cleaned up so that everything is as you would expect it to be, when coming from a different programming language.
If everything must be as you expected it to be in C, why not write in C?
Eh? But I *do* write in C (well C++) and use guile as a wrapper on that code. Despite this, I have lots and lots of scheme code where I use casual floating-point arithmetic -- sums, accumulation, division, logs. None of it is performance-critical, but I don't want it to be slow, either.
It does contribute to wall-clock time, and the python contingent are happy to thumb their noses at scheme when given the opportunity.
Simple things should be simple, should not require deep theoretical understanding.
Returning to (real? 1+i0) -- the "obvious solution" is to have not one but two predicates, here: (is-imaginary-part-zero? 1+i0) returns true and (is-complex-number-type? 1+i0) returns true.
The friction in the conversation seems to be that some people in the conversation are thinking is-real? means the first, and some the second. The naive programmer who is skimming the docs with glazed eyes will be .. surprised. Because naive programmers think that "real?" is a reference to the *type* and not a reference to the *value*.
Here's the other way to put it: (is-integer? 1.0) -- what should it return? #t or #f? I claim most naive programmers think of "integer" as a type, and not as a way of asking if the fractional part is zero.
Since we're in an argumentative mood: in 1982, I was privy to a conversation with the IBM xlC compiler developers, who had just implemented a fragment of tail calls for C. Bascally, the idea was that you didn't "buy a stack frame", if it wasn't really needed, and there was technical discussion on just how far this could be pushed in the direction of true tail calls.
If we drop the R6RS definition, one should - to help the compiler -
introduce the negative of `r6rs:real?' instead. Let us call it
`compnum?'. In optimizing implementations, it can just mean the C type
double complex.
> 2) Should `(compnum? 1.0+0i)` return #t or #f? In R6RS there is an example of `(real? -2.5+0i) => #t`, and in Chez Scheme `1.0+0i` and `1.0` are synonymous. Making `(compnum? 1.0+0i)` return #f helps with R6RS compatibility, but again introduces the notion of exact vs. inexact zero imaginary parts, albeit limited to the discussion of numeric literals.
This is my tentative definition of `compnum?':
(define compnum?
(lambda (obj)
(and (complex? obj)
(flonum? (real-part obj))
(flonum? (imag-part obj))
(eqv? obj (make-rectangular (real-part obj) (imag-part obj))))))
They only mean the same if the arguments are numbers of the same exactness.
(= 0 0.0) but (not (eqv? 0 0.0))
Here, I would like to stress that the exactness must also be the same.
However, the behavior of compnum? is still variable depending on the behavior of imag-part. What are the values of:(imag-part 1.0) => 0 or 0.0?(imag-part (make-rectangular 1.0 2)) => 2 or 2.0?In Chibi (R7RS) and Vicare (R6RS) the answers are 0 and 2, in Chicken (R7RS) and Chez (R6RS) the answers are 0.0 and 2.0, in Guile (R6RS) and Cyclone (R7RS) the answers are 0 and 2.0. I have not found any Schemes where the answers are 0.0 and 2, but it is conceivable.
Returning to (real? 1+i0) -- the "obvious solution" is to have not one but two predicates, here: (is-imaginary-part-zero? 1+i0) returns true and (is-complex-number-type? 1+i0) returns true.I don't think anyone disputes that. The question is, what happens when the imaginary part is inexact zero?The friction in the conversation seems to be that some people in the conversation are thinking is-real? means the first, and some the second. The naive programmer who is skimming the docs with glazed eyes will be .. surprised. Because naive programmers think that "real?" is a reference to the *type* and not a reference to the *value*.The Racket and R6RS folks think it will be less surprising if numbers with inexact imaginary parts are non-real. The R7RS folks think it will be less surprising if R5RS semantics remain the same.
So, there are two classes of predicates. -- "is object x of type t?" and "does the value of x have property p?" and `real?` is of the second class.
Well, does inexact zero have the property of being zero? seems like the answer is yes.
I'm happy with the r5rs/r7rs choice, ... however, is there some other predicate that allows me to ask the question "is 1.0+i0.0 of complex number type?" It seems like there isn't and it seems like this is needed?
BTW in guile(real? 1+0i) #t(real? 1.0+0.0i) #f(exact? 1+0i) #t(exact? 1+1i) #f ... really? WTF?That implies that 1+0i is being "automatically" converted to exact 1.
It's easy to have both predicates, the one that returns #t on 1.0+0.0i and the one that returns #f. The question is, what shall we call them to avoid confusing people?
This is my tentative definition of `compnum?':
(define compnum?
(lambda (obj)
(and (complex? obj)
(flonum? (real-part obj))
(flonum? (imag-part obj))
(eqv? obj (make-rectangular (real-part obj) (imag-part obj))))))
But what is really odd is that 1+1i becomes 1.0+1.0i. In other words, Guile supports exact and inexact real numbers but only inexact non-real numbers. R6RS seems to allow this.
On Tue, Aug 23, 2022 at 4:10 AM Linas Vepstas <linasv...@gmail.com> wrote:So, there are two classes of predicates. -- "is object x of type t?" and "does the value of x have property p?" and `real?` is of the second class.Well, if you want to put it that way. But I think of "has the property of being complex" and "belonging to (dynamic) type Complex" as equivalent, and the same for all other property/type pairs.
It's easy to have both predicates, the one that returns #t on 1.0+0.0i and the one that returns #f. The question is, what shall we call them to avoid confusing people?
I see this as being more like the distinction between vectors and lists. In a certain sense, lists and vectors are "exactly the same thing". You could have a scheme implementation that automatically converts between lists and vectors on a whim, whenever some clever optimization can be made. I doubt most users would be happy with that. They really expect `is-vector?` to return #f on lists, and would be surprised to find that sometimes, it's #t.
In programming practice, types refer to the (internal, hidden, possibly dynamic) structure of the storage location
I like Yongming Shen's answer:> In R6RS they are `real?` and `real-valued?`, with `(real? 1.0+0.0i) => #f` and `(real-valued? 1.0+0.0i) => #t`.
but it does leave open the question of how `complex?` and `complex-valued?` should behave.
On Tue, Aug 23, 2022 at 9:08 PM Alex Shinn <alex...@gmail.com> wrote:
On Wed, Aug 24, 2022 at 4:52 AM John Cowan <co...@ccil.org> wrote:It's easy to have both predicates, the one that returns #t on 1.0+0.0i and the one that returns #f. The question is, what shall we call them to avoid confusing people?real? and exactly-real? :)
In R6RS they are `real?` and `real-valued?`, with `(real? 1.0+0.0i) => #f` and `(real-valued? 1.0+0.0i) => #t`. And it is not quite just a matter of naming. Some generic arithmetic functions in the standard require some or all of their arguments to be "real numbers", and for those functions `real?` is used as the definition of real numbers.
John was repeating his question from 2.5 years ago which so far has received much discussion but not a single suggestion.
I advocate the point of view that useful modeling of mathematical numbers should, insofar as possible, not let the details of how those numbers are represented get in the way of their effective use in performing calculations, yet it seems clear that some of those details of representation could matter a lot to some users.
Thus I suggest that Scheme should provide easy ways for users to investigate how any number is represented.
Yet there are some problems in doing so. Let me elaborate:
I think all agree that the intent is to model the mathematical numbers as developed in a course on mathematical analysis.
In this modeling, we computer types find it useful to add placeholders to indicate that something has gotten bogus: Popular placeholders are nans, infs both signed and unsigned, and signed zeros. Other indications of wonkiness are often provided by various kinds of hardware floating-point exceptions or by suspicious users, but Schemers tend to lump them all into a single stick-on label of bogosity; namely, a cleared "exact bit" or its equivalent.
I use terms like "bogus" because I don't believe there is general agreement among mathematicians about how to introduce these concepts into the hierarchy of mathematical types.
There are lots of well-known representations. They include signed and unsigned integers of 8, 16, 32 and 64 bits, floating-point numbers in several formats of length 32, 64 and 80 bits, and several likely representations of bignums.
(And there used to be more -- does anyone remember the saying, "If you don't have 36 bits, you're not playing with a full DEC!"?)
Let me use the version of Wraith Scheme (R5 for the Macintosh)
In Wraith Scheme, a float is a tagged atom whose tag bits identify it as an IEEE 64-bit floating-point number.
what you might get if you parsed "1/3" according to recent Scheme language specifications
Scheme considers floats to contain only one kind each of nan, positive infinity, negative infinity and zero, even where there may be more than one bit pattern provided by the IEEE standard.
Any calculation that attempts to produce a long ratnum with zero denominator will return either an inf (for nonzero numerator) or a nan.
no long complex may have a nan as either real or imaginary part -- Wraith Scheme returns float nans instead of long complexes when calculations with long complexes produce nans.
Thus if you are working with integers, it is an advantage to keep them stored as fixnums as long as no overflow occurs
I suspect it will suffice, especially if you can't sleep. (Hmn, I am not sure that mousing these links will open them, you may have to copy and paste into your browser.)
Eventually she was able to exchange the '20 for a Jaguar XJ-S, so it was not in vain ...
It is sorta kinda based on the package system of Zetalisp/Common Lisp, and you can read about it in the "Package Management" section of the internals document. I think the primitives I built it with could be used to create an R5+-style library.
such as forgettable objects, a turtle-graphics system, and of course the Big Red Button.
A tagged aval comprises two adjacent machine words, one for the tag, and the other for a data element that is, e.g., some kind of atom or some kind of pointer into Scheme main memory, or perhaps something else.
When I wrote Pixie Scheme -- Wraith Scheme's predecessor -- I knew about implementations that stripped bits off pointers or machine words to use for tagging.
Do you have a reference for what those arguments were, or at least an idea of when they were made so that I can perhaps look them up in my archive of postings to this list?
On Wed, Aug 24, 2022 at 12:01 PM Linas Vepstas <linasv...@gmail.com> wrote:In programming practice, types refer to the (internal, hidden, possibly dynamic) structure of the storage locationIf something is hidden, what is the point in having a label for it? We never know the structure of the storage location in Scheme (or CL, Perl, Python, etc.) Maybe a complex number occupies two cells, maybe it doesn't. In Chicken, for example, an inexact complex number is two raw binary64s, but an exact complex number is typically two pointers with immutable contents (same size on 64-bit systems, not the same on 32-bit systems).
I'm ready to throw my hands in the air, and mutter "whatever". There seems to be some kind of misunderstanding.
The concept of "types" is a real thing.
I'm applying the same reasoning to reals and complex. If a function is supposed to return a real, I do not want to be surprised to find it sometimes returns 42+i0 If a function is supposed to return a complex number, it should do that, instead of sometimes returning an int.
For `example, 3 is an integer. Therefore 3 is also a rational, a real, and a complex [number]. The same is true of the Scheme numbers that model 3. For Scheme numbers, these types are defined by the predicatesnumber?
,complex?
,real?
,rational?
, andinteger?
.
But you might say "Ahh, don't worry, the reals and complex will automatically convert to one another as-needed under the covers!" If that is the case, then the predicates should also "convert automatically", and always respond to questions about type correctly. If, in your system, a function that returns strings might sometimes return 0, then (string? 0) should evaluate to #t.
--
You received this message because you are subscribed to a topic in the Google Groups "scheme-reports-wg2" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/scheme-reports-wg2/EAF0ZsFeqmU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to scheme-reports-...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/scheme-reports-wg2/CAD2gp_QxG4Q8rz7VKbgTohGWZLmsQb-UNOorz9Goha6qzOgrJw%40mail.gmail.com.
--
You received this message because you are subscribed to the Google Groups "scheme-reports-wg2" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scheme-reports-...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/scheme-reports-wg2/CAEYrNrS3HRFj%3DJEFi%2BY8A3fa%3D8eK2KaSHMFgVgoP%3Ds-EHda1sg%40mail.gmail.com.
Herein I will use the word "type" only with meaning (1), and will coin the phrase "storage class" to substitute for its use with meaning (2).
We might use the syntax "+0" and "-0" -- as distinguished from just plain "0" -- when printing out these bit patterns
(A) Any Scheme number whose imaginary part is either "+0" or "-0" is clearly *not* representing a mathematical number of type real (because its imaginary part is non-zero), and the predicate "real?" should return #f for it
-- *BUT* -- any Scheme number whose imaginary part is just plain "0" clearly *is* representing a mathematical number of type real, and the predicate "real?" should return #t for it.
(B) Suppose (number->string x) prints out "42+0i". Does x have an imaginary part that is the non-zero value "+0", or is it just plain "0"?
the fact that THE PRESENT SYNTAX FOR UNDERFLOWS IS CONFUSING.
(C) Procedure imag-part should return a value for every Scheme number, regardless of its storage class. E.g., even if Scheme is representing the mathematical number 3 and happens to use a 64-bit signed fixnum to store its representation, then (imag-part 3) should return 0.
(a) Predicates to test for infs, nans and underflows.
(b) A procedure to return the name of the storage class used for a particular number? The language specification could define particular names for common storage classes, including, e.g., "ieee-64-bit-floating-point" or "signed-64-bit-integer", and allow implementations to add others as needed.
(c) A procedure to return the actual bit pattern(s) used for a particular number, possibly as a list of bytes or words.
I further suggest that predicates and other procedures that deal with numbers according to mathematical type (in the sense of (1) above) should not special-case inputs or outputs on the basis of storage class.
Linas Vepstas wrote:
> If a function is supposed to return a real, I do not want to be surprised to find it sometimes returns 42+i0.
The trouble is, that when read as a mathematical number, 42+i0 *is* real -- no ifs, ands, buts or maybes.
Part of the problem is that the discussion has been using two different meanings of the word "type": (1) "Type" is used to mean the kind of mathematical number that a Scheme implementation is intending to model, and (2) "type" is used to mean the pattern of bits that the Scheme implementation is using to store its representation of that number.
For example, there is a function that takes inexact reals as an argument, should it throw an exception if given an exact argument, or should it implicitly convert?
If there's a function that "usually" returns inexact reals, is it ever allowable for it to return a rectangular complex number?
Both of the above are determined by a "directionality" in the tower: exact numbers can be converted into inexact ones, without ambiguity or confusion, but not the other way.
Sigh, well, here I throw up my hands again.
I was using the word "type" in sense number (3) -- that thing that the type-theory people call a type. Type-theory types are neither (1) nor (2) but are something else again.
I object to the use of the concept of a "mathematical number" in these conversations, since that is an extremely vague concept, to the point of being unusable. What the heck, are you proposing that scheme implement finite function fields, which are technically "mathematical numbers"?
What about irrational numbers?