Skip to content

Add to Float arithmetic operators, is_finite,..., round, min/max.#1794

Merged
alainfrisch merged 20 commits intoocaml:trunkfrom
Chris00:float
Oct 23, 2018
Merged

Add to Float arithmetic operators, is_finite,..., round, min/max.#1794
alainfrisch merged 20 commits intoocaml:trunkfrom
Chris00:float

Conversation

@Chris00
Copy link
Copy Markdown
Member

@Chris00 Chris00 commented May 23, 2018

Remark: The round function calls to C because the latter is usually implemented as ASM. Note that naive round functions do not work.

@dbuenzli
Copy link
Copy Markdown
Contributor

Since this PR adds a wealth of predicates could you maybe also add the is_integer predicate ? It's useful in various conversion scenarios.

@nojb
Copy link
Copy Markdown
Contributor

nojb commented May 23, 2018

The AppVeyor failure is due to old versions of msvc not having round in their standard library. You will need to provide an implementation of round for them. @dra27 will probably know the full story about this.

Also, I think tests will be needed for the new functions.

@Chris00
Copy link
Copy Markdown
Member Author

Chris00 commented May 23, 2018

old versions of msvc not having round in their standard library. You will need to provide an implementation of round for them

I saw... ☹ I thought that was something of the past...

I'll try to do the additional work ASAP.

@Chris00 Chris00 force-pushed the float branch 5 times, most recently from a32d1d2 to 7b09918 Compare May 24, 2018 09:38
@Chris00
Copy link
Copy Markdown
Member Author

Chris00 commented May 24, 2018

@dbuenzli Wish granted! ☺

Comment thread stdlib/float.ml
Comment thread stdlib/float.mli
Comment thread stdlib/float.ml
Comment thread stdlib/float.ml
@Chris00
Copy link
Copy Markdown
Member Author

Chris00 commented Jun 1, 2018

@mshinwell It seems Flambda transforms -0. in +0. through an incorrect optimization. Here is an example that does not use the new primitives (it is important that the condition of the first if be “opaque”):

let z =
  let x = -0. and y = +0. in
  if mod_float x 1. >= 0. then
    x
  else if false then x else y

let () =
  printf "%g\n" (1. /. z)

prints inf (it should be -inf).

@xclerc
Copy link
Copy Markdown
Contributor

xclerc commented Jun 1, 2018

Here is a minimal reproduction case:

let f b =
  let x = -0. and y = +0. in
  if b then x else y

The problem is that flambda, when meeting the values of the
branches, uses = over float values. Thus considering that both
branches return the same value.

I will open a PR fixing the issue shortly.

@xclerc
Copy link
Copy Markdown
Contributor

xclerc commented Jun 1, 2018

#1810 fixes the issue mentioned in the previous comments.

@Chris00
Copy link
Copy Markdown
Member Author

Chris00 commented Jun 2, 2018

Rebased on trunk.

Comment thread stdlib/float.mli Outdated
when [x] or [y] is [nan]. Moreover [max (-0.) (+0.) = +0.] *)

val minmax : float -> float -> float * float
(** [minmax x y] returns [(x, y)] is [x <= y] and [(y, x)] otherwise.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"(...) returns (...) is (...)" should be "(...) returns (...) if (...)".

Comment thread stdlib/float.mli Outdated
= "caml_nextafter_float" "caml_nextafter" [@@unboxed] [@@noalloc]
(** [nextafter x y] returns the next representable floating-point
value following [x] in the direction of [y]. If [y] is less than
[x], these functions will return the largest representable number
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"these functions" seems to be from the C documentation...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is – the documentation is heavily inspired from the C one.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant: shouldn't it be "this function"?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modified it in the commit below.

Comment thread stdlib/float.mli Outdated

external trunc : float -> float = "caml_trunc_float" "caml_trunc"
[@@unboxed] [@@noalloc]
(** [trunc x] round [x] to the nearest integer whose absolute value is
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"round" should be "rounds" for consistency.

Comment thread stdlib/float.mli Outdated
(** [is_nan x] returns [true] if and only if [x] represents a [nan]. *)

val is_integer : float -> bool
(** Says whether the floating point is an integer. *)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Says" should be "Say" for consistency.

Comment thread stdlib/float.mli Outdated
(** [is_infinite x] says whether [x] is [infinity] or [neg_infinity]. *)

val is_nan : float -> bool
(** [is_nan x] returns [true] if and only if [x] represents a [nan]. *)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

("[nan]" instead of "a [nan]"?)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what is the better (given that there are several NaNs, even though we do not distinguish them here).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure, but I think the documentation tends
to use "nan" rather than "a nan" in other places.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can change it if you wish. Or maybe reformulate it as [x] is [nan].

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not have a strong opinion on this question;
maybe @Octachron or @gasche will.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like [x] is [nan]: it has the advantage of being correct when expanding it to [x] is not a number; moreover the Pervasives documentation already uses this wording.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread stdlib/float.mli Outdated
value following [x] in the direction of [y]. If [y] is less than
[x], these functions will return the largest representable number
less than [x]. If [x] equals [y], the function returns [y].
If [x] or [y] is a [nan], a [nan] is returned. *)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

("[nan]" instead of "a [nan]"?)

@Chris00
Copy link
Copy Markdown
Member Author

Chris00 commented Jun 21, 2018

Rebased.

Copy link
Copy Markdown
Contributor

@dbuenzli dbuenzli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Chris00 !

@Chris00
Copy link
Copy Markdown
Member Author

Chris00 commented Oct 19, 2018

@gasche Fine with me.

@xclerc
Copy link
Copy Markdown
Contributor

xclerc commented Oct 19, 2018

I have to say I have mixed feelings about pred/succ.

The introduction of one, zero, and minus_one to
be able to abstract over integer or float modules is fine,
but then the semantics of pred/succ is so different
that it is likely to cause more or less subtle problems.

@xclerc
Copy link
Copy Markdown
Contributor

xclerc commented Oct 19, 2018

To be clear, I would prefer to have the functions defined as:

let pred x = x -. 1.0
let succ x = x +. 1.0

@dbuenzli
Copy link
Copy Markdown
Contributor

but then the semantics of pred/succ is so different

It's not different it gives you the next value greater than the current one in the domain of the data type.

@xclerc
Copy link
Copy Markdown
Contributor

xclerc commented Oct 19, 2018

I do understand your point of view, but I suspect some
code (arguably originally written with integers in mind)
would assume that succ x is x + 1.

@dbuenzli
Copy link
Copy Markdown
Contributor

And maybe some code would simply like to get a value that is greater than the one that is given as argument. As far as I'm concerned pred and succ could also be defined on say type t = A | B | C.

@xclerc
Copy link
Copy Markdown
Contributor

xclerc commented Oct 19, 2018

I would also be fine with pred/succ on type t = A | B | C
because this type maps to a subset of integers...

My point is that most of the existing code abstracting over
IntK.{pred,succ} expects the natural properties between
these functions and addition to hold. Passing Float will then
lead to unexpected results.

I just wanted to share a concern, you and the author of the
PR will decide which definitions should be used.

@Chris00
Copy link
Copy Markdown
Member Author

Chris00 commented Oct 19, 2018

My point is that most of the existing code abstracting over IntK.{pred,succ}

Are there examples?

In the end, the question is what semantics we want to have for pred and succ. As aliases for x -> X.add x X.one and x -> X.sub x X.one, I am not sure they are interesting — and to me, the successor is the one that comes after, which depends on the type and does not necessarily correspond to “+1”.

@xclerc
Copy link
Copy Markdown
Contributor

xclerc commented Oct 19, 2018

Are there examples?

To be fair, most of the examples I have in mind are so
closely related to integers that you would probably not
consider them. In Core, most occurrences of succ are
related to counters so one could reasonably move from
Int to Int{32,64} but not to Float.

In the end, the question is what semantics we want to have for pred and succ. As aliases for x -> X.add x X.one and x -> X.sub x X.one, I am not sure they are interesting — and to me, the successor is the one that comes after, which depends on the type and does not necessarily correspond to “+1”.

I won't say they are interesting as aliases, merely that they
are less surprising: replace Int32 with Float in a functor
application and you will approximately get the same results.

@dbuenzli
Copy link
Copy Markdown
Contributor

I really prefer if we keep succ and pred as a convention for an ordered walk over the values of a type. As @Chris00 mentions I see little point of having that as an alias for add x one.

Comment thread stdlib/float.mli
Comment thread testsuite/tests/lib-float/test.ml
@xclerc
Copy link
Copy Markdown
Contributor

xclerc commented Oct 19, 2018

Out of curiosity, is nan the predecessor/successor
of a non-nan value?

@Chris00
Copy link
Copy Markdown
Member Author

Chris00 commented Oct 19, 2018

@xclerc no.

@Chris00
Copy link
Copy Markdown
Member Author

Chris00 commented Oct 19, 2018

succ x is nanx is nan

@xclerc
Copy link
Copy Markdown
Contributor

xclerc commented Oct 19, 2018

@Chris00 thanks!

@alainfrisch alainfrisch merged commit 00f9739 into ocaml:trunk Oct 23, 2018
@alainfrisch
Copy link
Copy Markdown
Contributor

Thanks @Chris00 for your efforts!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.