I am now playing around with nan, for example I have
already learnt this trick:
?- X is 1 rdiv 2 + nan.
ERROR: Arithmetic: evaluation error: `undefined'
?- set_prolog_flag(float_undefined, nan).
true.
?- X is 1 rdiv 2 + nan.
X = 1.5NaN.
But here there seems to be a gap:
?- X is 0 rdiv 0.
ERROR: Arithmetic: evaluation error: `zero_divisor'
?- set_prolog_flag(float_zero_div, nan).
ERROR: Domain error: `flag_value' expected, found `nan'
The problem is that 0/0 is listed here as undefined or invalid,
with some remark that this can mean a value NaN:
An undefined exception can occur with:
0/0 was zero_divisor in 13211-1
These correspond to the cases where IEEE 754 would
produce the INVALID exception and produce a NaN. http://eclipseclp.org/Specs/core_update_float.html
But SWI-Prolog seems to not follow the undefined appraoch,
otherwise my previous set_prolog_flag(float_undefined, nan)
would have had an effect. But then a float_zero_div is not
specific enough, a float_zero_div_zero flag or a float_invalid flag
would be needed to address 0/0 and solve the problem. One could
then completely follow Joachim Schimpfs proposal?
Strange if I switch off the division by zero error in my library(arithmetic/ratio)
and replace it by a normalization N/0 ~~> sign(N)/0. I can define:
nan(0#0).
And then the following works, without changing anything else in the
my library(arithmetic/ratio):
?- X is nan.
X = 0#0.
?- X is 1 rdiv 2+nan.
X = 0#0.
?- X is 1 rdiv 2-nan.
X = 0#0.
?- X is 1 rdiv 2*nan.
X = 0#0.
?- X is 1 rdiv 2 rdiv nan.
X = 0#0.
?- X is nan rdiv (1 rdiv 2).
X = 0#0.
?- X is 0 rdiv 0.
X = 0#0.
Next testing would be what happens if define this infinity,
inspired by Leonhard Euler:
inf(1#0).
Edit 21.01.2023
Ok here what infinity can do again without chaning the library(arithmetic/ratio), just
using the school book rules for rational arithmetic for Q*, plus the different normalization:
?- X is inf.
X = 1#0.
?- X is -inf.
X = -1#0.
?- X is 1 rdiv 2+inf.
X = 1#0.
?- X is 1 rdiv 2*inf.
X = 1#0.
?- X is 1 rdiv 2-inf.
X = -1#0.
?- X is 1 rdiv 2 rdiv inf.
X = 0.
?- X is inf rdiv (1 rdiv 2).
X = 1#0.
?- X is inf-inf.
X = 0#0.
?- X is inf*inf.
X = 1#0.
?- X is inf rdiv inf.
X = 0#0.
The above is all fine, if we read 1#0 as infinity and 0#0 as NaN again.
What goes wrong is for example:
?- X is inf+inf.
X = 0#0.
It kind of wraps around. The above could be fixed by an
additional rule in rational number addition.
First point is that the only permissable values of the float_zero_div flag are error and infinity as prescribed by the Eclipse document you referenced.
The second point is that rdiv is a rational function and (currently) not subject to the float_* flags. That could be changed but I think you would then be obliged to do the same for all the integer divide functions: \\, div, rem and mod. Having just lost the battle for correct comparison, I have no interest in that fight. Also note that the Eclipse proposal does not mention any of the above functions - its title is " Proposal for Prolog Standard core update wrt floating point arithmetic".
However, I think there is a bug in the SWIP / function: given the float_zero_div is set to infinity, 0/0 evaluates to infinity while 0.0/0 evaluates to NaN; the latter is correct IMO.
I don’t doubt that Q*, and Z*, can be implemented. My only question is whether there’s a tangible benefit for doing so in SWIP, particularly given that the underlying supporting libraries (GMP) only support Q and Z.
I only tried to make the point that the current rdiv behaviour is consistent with the SWIP implementation of rationals as Q, contrary to what the title of this thread might suggest.
I then basically asked the question if extending Q to Q* (by changing this behaviour) is such a good idea, why isn’t extending Z to Z*? It can be a slippery slope.
You might consider this FUD. I don’t, but I’ll leave it to others to make their own decision.
rdiv/2 was introduced as a quick way to introduce rational numbers without introducing a new data type. It is there only for this reason and should be deprecated now that we have atomic rational numbers. Surely it should do nothing with floats.
I definitely think there is an issue in all the “noise”. @j4n_bur53 correctly points out that 0 rdiv 0 generates a zero_divisor error but it really should generate an undefined error.
0/0 has the same problem, as I pointed out elsewhere. This is a bigger issue IMO because it is a direct violation of the Eclipse proposal. And combined with the fact that it is subject to to the float_* IEEE continuation flags can actually produce erroneous results. (If either argument is 0.0, it works correctly.)
Also 0 div 0, 0//0, 0 rem 0 and 0 mod 0 all have the same issue as 0 rdiv 0, incorrect error code.
Regardless of the status of rdiv, these should all be fixed IMO.
Although true, I don’t think I said (or insisted) anything like that, did I? Now in this case they are all undefined so their behaviour should be consistently reflect that.
As a consequence, if any functions result in a continuation value, rather than an error, any continuation values for equivalent arguments should be arithmetically equivalent. For example, 0.0/0.0 =:= 0/0; currently that’s not true.
Why do we care about the exception? ECLiPSe simply seems to generate an abort, but possibly mine is too old (6.1). The ISO standard demands for an evaluation_error(zero_divisor) exception. Shouldn’t we simply consider this to be subsumed by evaluation_error(undefined)? Does it matter much? Isn’t the only thing that really matters what happens if we set the float flags?
This, I think is wrong because integer division acts as float division if the integers to not perfectly divide or prefer_rationals is enabled.
?- set_prolog_flag(float_undefined, nan).
true.
?- A is 0 / 0.0.
A = 1.5NaN
?- A is 0 / 0.
ERROR: Arithmetic: evaluation error: `zero_divisor'.
Now this is a bit inconsistent as we have the flag float_zero_div that can be infinity, but clearly 0/0[.0] should not be infinity. I can live with that.
A bit more troublesome is how to deal with 0/0 if prefer_rationals is enabled. We do not have a rational equivalent to NaN, and generating a float is also not what I want. An exception seems the only way out. We could make an exception if automatic conversion of rational to float for large rationals is also enabled as this means the application is prepared to get either a rational or float result.
JavaScript is quite a bit incoherent in a lot of respects; and its design decisions shouldn’t be used as a paradigm for anything.
For some examples of JavaScript being incoherent: Wat (starting at 1:25)
1995 - Brendan Eich reads up on every mistake ever made in designing a programming language, invents a few more, and creates LiveScript. Later, in an effort to cash in on the popularity of Java the language is renamed JavaScript. …
— A Brief, Incomplete, and Mostly Wrong History of Programming Languages
I think there are cases, e.g., implementing extended rationals at the Prolog level, where you want to distinguish between the two cases, e.g., to convert undefined errors to a custom NaN value and zero_divisor to a custom infinity value, in an error handler. Also the Eclipse proposal (which makes a lot of sense to me) does define separate errors for these two cases, and it is consistent with IEEE behaviour.
I avoid this issue by thinking of the floating point special values as polymorphic values, even if they are implemented as floats. This all seems to work fine in mixed mode expressions (`+,-,*,/,**,elementary functions, …), i.e., generating a “float” for these special cases is exactly what I want.
The only downside is that the builtin numeric type tests reflect the underlying implementation, but that’s much more preferable than generating exceptions in expression evaluation that have to be avoided a priori or dealt with in an exception handler.
That leaves type specific functions as an open question - should they be allowed to return a polymorphic infinity or nan? I kind of think they should, but don’t really have any compelling use cases. Maybe these functions should even accept a polymorphic infinity as an argument, e.g., 0 is 1 div inf.
So the status quo has some issues at the intellectual level, but practically it seems to work fine (for me).
What I couldn’t figure out yet, whether modern programming
languages such as Python or JavaScript have some distinction
between things like quite-nan and non-quite-nan.
On the other hand, their less than or equal also looses
totality, the order relation is not anymore total, which is
again in accordance to some mathematical logic
Edit 25.01.2023
What are free logics? Basically logics with terms that
are non-denoting, which poses some problems since
terms are not anymore always single valued,
a value might be absent:
Free Logic - Stanford Encyclopedia of Philosophy
Classical logic requires each singular term to denote
an object in the domain of quantification—which is usually
understood as the set of “existing” objects. Free logic does not. https://plato.stanford.edu/entries/logic-free/
They have some suggestion how predicates should behave
in the context of partial functions. See section 3. Semantics,
4.1 Problems with Primitive Predicates, 5.2 Logics with
IEEE has a standard for how NaN behaves. They put lots of thought into the matter. In general we should just follow them. If we have a rationalNan it should behave like float NaN. Whether the two are the same value or not doesn’t concern me. As far as I am concerned they can both be Bottom.
Most often well-made programs don’t encounter NaN. Library functions that encounter NaN due to mismanaged arguments usually would yield NaN rather than a number, so the poorly made program is aware of the problem and can be changed. If it wasn’t for the fact of interop with other languages and the processor, fail/0 would be appropriate, but we have a standard.
Motivation to blur bigint and rational, was that the rational/1
test predicate in SWI-Prolog succeeds also for integer values:
?- rational(123).
true.
?- X is 123r1.
X = 123.
So if you add something to Z you have also added it to Q,
more or less. This is I guess in accordance with CommonLisp,
which also demotes a rational Zr1 to an integer Z. The Prolog
cannonical form of a rational with denominator 1 is an integer.
It might not be the GMP canonical form. But if the viewpoint
inf = 1/0 enters the picture, canonicalization needs to be
extended. In a little prototype I used this canonicalization:
The above covers NaN, inf and -inf. Not sure what to do with -0.
Edit 27.01.2023
Interesting find yesterday the Go programming language
has -0, inf and -inf in their big float, but not NaN.