Feature #6973

Add an #integral? method to Numeric to test for whole-number values

Added by Alex Young over 1 year ago. Updated over 1 year ago.

[ruby-core:47409]
Status:Assigned
Priority:Normal
Assignee:Kenta Murata
Category:core
Target version:next minor

Description

Numeric#integer? checks whether an instance is an Integer. It is often useful to check whether the value of a non-Integer variable is actually a whole number, and the #integer? method doesn't help here.

This patch adds Numeric#integral?, which performs this check.

integral.patch Magnifier (2.43 KB) Alex Young, 09/04/2012 06:03 AM


Related issues

Related to ruby-trunk - Feature #5310: Integral objects Assigned 09/13/2011

History

#1 Updated by Joshua Ballanco over 1 year ago

Just a suggestion, but I don't think "integral" really explains what you want this method to do, and it is confusingly close to "integer?". Why not Numeric#whole_number?

Personally, I'm not entirely convinced this method is a good idea. With Floats, you can't really guarantee that "4.0" is the same as "4". It could actually be "4.00000000000000001" but the precision is lost. If you want to know if my_float.floor == my_float, that check isn't so hard to do and also has a very specific meaning. I could maybe see including this sort of method in BigDecimal or Rational (though in that case it would essentially be the same as my_rational.denomenator == 1). What sort of use case were you picturing?

Finally, just a note on the patch: there are easier (i.e. without allocation) ways to determine if a float, bigdecimal, or rational is a whole number. Why not give all of the subclasses in the stdlib specific implementations in addition to the generic implementation in Numeric?

#2 Updated by Alex Young over 1 year ago

On 04/09/12 11:01, jballanc (Joshua Ballanco) wrote:

Issue #6973 has been updated by jballanc (Joshua Ballanco).

Just a suggestion, but I don't think "integral" really explains what
you want this method to do, and it is confusingly close to
"integer?". Why not Numeric#whole_number?

I could live with that. I think the problem is actually that #integer?
is unfortunately-named, but let's put that to one side :-)

Personally, I'm not entirely convinced this method is a good idea.
With Floats, you can't really guarantee that "4.0" is the same as
"4". It could actually be "4.00000000000000001" but the precision is
lost.

This is actually not true. If you have a float with a value of 4.0,
then that genuinely is the same as 4. The whole number value "4" has
a precise representation in floating point format, and if you have that
value then it makes no sense to say that what you have might not be
equal to 4.

Floats are precise, just like Integers are. Operations on floats are
not accurate, and that's where the confusion comes from. That's
inherent to the representation and something you've got to deal with
anyway, so I certainly don't see this test as making that problem worse.

If you want to know if my_float.floor == my_float, that check
isn't so hard to do and also has a very specific meaning. I could
maybe see including this sort of method in BigDecimal or Rational
(though in that case it would essentially be the same as
my_rational.denomenator == 1). What sort of use case were you
picturing?

This came up in #6958, specifically because BigDecimal's #integer? as
inherited from Numeric is confusing.

Finally, just a note on the patch: there are easier (i.e. without
allocation) ways to determine if a float, bigdecimal, or rational is
a whole number. Why not give all of the subclasses in the stdlib
specific implementations in addition to the generic implementation in
Numeric?

Sure. Interface before implementation.

--
Alex

---------------------------------------- Feature #6973: Add
an #integral? method to Numeric to test for whole-number values
https://bugs.ruby-lang.org/issues/6973#change-29171

Author: regularfry (Alex Young) Status: Open Priority: Normal
Assignee: Category: Target version: 2.0.0

Numeric#integer? checks whether an instance is an Integer. It is
often useful to check whether the value of a non-Integer variable is
actually a whole number, and the #integer? method doesn't help here.

This patch adds Numeric#integral?, which performs this check.

#3 Updated by Joshua Ballanco over 1 year ago

=begin

Floats are precise, just like Integers are. Operations on floats are
not accurate, and that's where the confusion comes from. That's
inherent to the representation and something you've got to deal with
anyway, so I certainly don't see this test as making that problem worse.

Ok, fair point. Ruby already doesn't protect you from a lack of knowledge of floats, so I can accept that this method wouldn't either.

This came up in #6958, specifically because BigDecimal's #integer? as
inherited from Numeric is confusing.
...
Sure. Interface before implementation.

Actually, as I got to thinking about this, I think the variety of different ways this can be determined efficiently for different numeric types might justify having this as a method. That, of course, presupposes that each numeric type gets a concrete, efficient implementation.
=end

#4 Updated by Koichi Sasada over 1 year ago

  • Category set to core
  • Assignee set to Yusuke Endoh

mame-san, could you judge on it?

#5 Updated by Yusuke Endoh over 1 year ago

  • Assignee changed from Yusuke Endoh to Kenta Murata
  • Target version changed from 2.0.0 to next minor
  • Status changed from Open to Assigned

I think mrkn is eligible to handle this ticket.
The following is my personal comment.

regularfry (Alex Young) wrote:

Personally, I'm not entirely convinced this method is a good idea.
With Floats, you can't really guarantee that "4.0" is the same as
"4". It could actually be "4.00000000000000001" but the precision is
lost.

This is actually not true. If you have a float with a value of 4.0,
then that genuinely is the same as 4. The whole number value "4" has
a precise representation in floating point format, and if you have that
value then it makes no sense to say that what you have might not be
equal to 4.

Floats are precise, just like Integers are. Operations on floats are
not accurate, and that's where the confusion comes from. That's
inherent to the representation and something you've got to deal with
anyway, so I certainly don't see this test as making that problem worse.

Your points is valid as long as we can distinguish a Float that includes
error from a Float that includes no error, such as, just created by a
Float literal, or by only accurate operations (no overflow, no indivisible
division, ...).
But in fact, we cannot distinguish them. We conservatively have to think
that any Float object includes an error, I think.

Yusuke Endoh mame@tsg.ne.jp

#6 Updated by Alex Young over 1 year ago

On 27/10/12 05:26, mame (Yusuke Endoh) wrote:

Issue #6973 has been updated by mame (Yusuke Endoh).

Status changed from Open to Assigned
Assignee changed from mame (Yusuke Endoh) to mrkn (Kenta Murata)
Target version changed from 2.0.0 to next minor

I think mrkn is eligible to handle this ticket.
The following is my personal comment.

regularfry (Alex Young) wrote:

Personally, I'm not entirely convinced this method is a good idea.
With Floats, you can't really guarantee that "4.0" is the same as
"4". It could actually be "4.00000000000000001" but the precision is
lost.

This is actually not true. If you have a float with a value of 4.0,
then that genuinely is the same as 4. The whole number value "4" has
a precise representation in floating point format, and if you have that
value then it makes no sense to say that what you have might not be
equal to 4.

Floats are precise, just like Integers are. Operations on floats are
not accurate, and that's where the confusion comes from. That's
inherent to the representation and something you've got to deal with
anyway, so I certainly don't see this test as making that problem worse.

Your points is valid as long as we can distinguish a Float that includes
error from a Float that includes no error, such as, just created by a
Float literal, or by only accurate operations (no overflow, no indivisible
division, ...).
But in fact, we cannot distinguish them. We conservatively have to think
that any Float object includes an error, I think.

So if I knowingly and correctly construct a calculation which cannot
contain such an error, Ruby should tell me that it's wrong?

--
Alex

#7 Updated by Alexey Muranov over 1 year ago

Floats are precise, just like Integers are. Operations on floats are
not accurate, and that's where the confusion comes from. That's
inherent to the representation and something you've got to deal with
anyway, so I certainly don't see this test as making that problem worse.

Your points is valid as long as we can distinguish a Float that includes
error from a Float that includes no error, such as, just created by a
Float literal, or by only accurate operations (no overflow, no indivisible
division, ...).
But in fact, we cannot distinguish them. We conservatively have to think
that any Float object includes an error, I think.

So if I knowingly and correctly construct a calculation which cannot
contain such an error, Ruby should tell me that it's wrong?

I guess the question is: what would be a point in calling this method on a Float? A Float value is rarely exact in the sense that it rarely coincides with the real-world value or mathematical value which it models.

#8 Updated by Matthew Kerwin over 1 year ago

On 30 October 2012 08:12, alexeymuranov (Alexey Muranov)
redmine@ruby-lang.org wrote:

Issue #6973 has been updated by alexeymuranov (Alexey Muranov).

Floats are precise, just like Integers are. Operations on floats are
not accurate, and that's where the confusion comes from. That's
inherent to the representation and something you've got to deal with
anyway, so I certainly don't see this test as making that problem worse.

Your points is valid as long as we can distinguish a Float that includes
error from a Float that includes no error, such as, just created by a
Float literal, or by only accurate operations (no overflow, no indivisible
division, ...).
But in fact, we cannot distinguish them. We conservatively have to think
that any Float object includes an error, I think.

So if I knowingly and correctly construct a calculation which cannot
contain such an error, Ruby should tell me that it's wrong?

I guess the question is: what would be a point in calling this method on a Float? A Float value is rarely exact in the sense that it rarely coincides with the real-world value which it models.

What if it had an (optional?) parameter for precision? I know in the
past I've wanted to know whether the value stored in a float looked
like an integer to a certain number of places.

I believe that would make it more clear that we're not necessarily
saying that the real-world value we're modeling is exactly an integer;
rather that it is near enough for our current purposes.

If optional, I suppose the default would be "as many places as are
represented by this float," or (self.to_i == self)

--
Matthew Kerwin, B.Sc (CompSci) (Hons)
http://matthew.kerwin.net.au/
ABN: 59-013-727-651

"You'll never find a programming language that frees
you from the burden of clarifying your ideas." - xkcd

#9 Updated by Alex Young over 1 year ago

On 29/10/12 22:12, alexeymuranov (Alexey Muranov) wrote:

Issue #6973 has been updated by alexeymuranov (Alexey Muranov).

Floats are precise, just like Integers are. Operations on floats are
not accurate, and that's where the confusion comes from. That's
inherent to the representation and something you've got to deal with
anyway, so I certainly don't see this test as making that problem worse.

Your points is valid as long as we can distinguish a Float that includes
error from a Float that includes no error, such as, just created by a
Float literal, or by only accurate operations (no overflow, no indivisible
division, ...).
But in fact, we cannot distinguish them. We conservatively have to think
that any Float object includes an error, I think.

So if I knowingly and correctly construct a calculation which cannot
contain such an error, Ruby should tell me that it's wrong?

I guess the question is: what would be a point in calling this method on a Float? A Float value is rarely exact in the sense that it rarely coincides with the real-world value which it models.

I explained this upthread. Float values are always exact. Float
operations are not.

--
Alex


Feature #6973: Add an #integral? method to Numeric to test for whole-number values
https://bugs.ruby-lang.org/issues/6973#change-31933

Author: regularfry (Alex Young)
Status: Assigned
Priority: Normal
Assignee: mrkn (Kenta Murata)
Category: core
Target version: next minor

Numeric#integer? checks whether an instance is an Integer. It is often useful to check whether the value of a non-Integer variable is actually a whole number, and the #integer? method doesn't help here.

This patch adds Numeric#integral?, which performs this check.

#10 Updated by Alexey Muranov over 1 year ago

regularfry (Alex Young) wrote:

So if I knowingly and correctly construct a calculation which cannot
contain such an error, Ruby should tell me that it's wrong?

Alex, can you give an example, please?

#11 Updated by Alex Young over 1 year ago

On 31/10/12 13:03, alexeymuranov (Alexey Muranov) wrote:

Issue #6973 has been updated by alexeymuranov (Alexey Muranov).

regularfry (Alex Young) wrote:

So if I knowingly and correctly construct a calculation which cannot
contain such an error, Ruby should tell me that it's wrong?

Alex, can you give an example, please?

Trivially? x * 2.0 for x small enough not to overflow.

There's a more interesting vein of examples where this sort of thing
crops up if you google for "error-free transformations", like the 2Sum
and Fast2Sum algorithms.

--
Alex


Feature #6973: Add an #integral? method to Numeric to test for whole-number values
https://bugs.ruby-lang.org/issues/6973#change-32110

Author: regularfry (Alex Young)
Status: Assigned
Priority: Normal
Assignee: mrkn (Kenta Murata)
Category: core
Target version: next minor

Numeric#integer? checks whether an instance is an Integer. It is often useful to check whether the value of a non-Integer variable is actually a whole number, and the #integer? method doesn't help here.

This patch adds Numeric#integral?, which performs this check.

Also available in: Atom PDF