Project

General

Profile

Feature #12753

Useful operator to check bit-flag is true or false

Added by tagomoris (Satoshi TAGOMORI) over 1 year ago. Updated 5 months ago.

Status:
Closed
Priority:
Normal
Target version:
[ruby-core:77251]

Description

Ruby's 0 is truthy value. It's useful for many cases, but it's confusing and I made many bugs when I'm writing code to handle binary data, because my thought is almost same with one to write C code in such situation.

n = get_integer_value
if n & 0b10100000
  # code for the case when flag is true
else
  # never comes here :(
end

IMO it's very useful to have methods for such use-cases, like #and? and #xor? (#or? looks not so useful... I can't imagine the use case of this operator, but it's better to have for consistency).

n = get_integer_value
case
when n.and?(0b10000000)
  # negative signed char
when n.and?(0b01110000)
  # large positive
else
  # small positive
end

Associated revisions

Revision 0da34dbb
Added by naruse (Yui NARUSE) 6 months ago

Integer#allbits?, Integer#anybits?, Integer#nobits? [Feature #12753]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61147 b2dd03c8-39d4-4d8f-98ff-823fe69b080e

Revision 61147
Added by naruse (Yui NARUSE) 6 months ago

Integer#allbits?, Integer#anybits?, Integer#nobits? [Feature #12753]

Revision 61147
Added by naruse (Yui NARUSE) 6 months ago

Integer#allbits?, Integer#anybits?, Integer#nobits? [Feature #12753]

Revision 907508b2
Added by marcandre (Marc-Andre Lafortune) 5 months ago

Integer#{any|all|no}_bits: Fix coercion. Add specs [#12753]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61305 b2dd03c8-39d4-4d8f-98ff-823fe69b080e

Revision 61305
Added by marcandre (Marc-Andre Lafortune) 5 months ago

Integer#{any|all|no}_bits: Fix coercion. Add specs [#12753]

Revision 61305
Added by marcandre (Marc-Andre Lafortune) 5 months ago

Integer#{any|all|no}_bits: Fix coercion. Add specs [#12753]

History

#1 [ruby-core:77253] Updated by shyouhei (Shyouhei Urabe) over 1 year ago

  • Description updated (diff)

#2 [ruby-core:78314] Updated by matz (Yukihiro Matsumoto) over 1 year ago

I understand the demand. But and? is an unacceptable name.
Any idea?

Matz.

#3 [ruby-core:78315] Updated by naruse (Yui NARUSE) over 1 year ago

What about bittest?

#4 [ruby-core:78319] Updated by matz (Yukihiro Matsumoto) over 1 year ago

bittest? sounds reasonable. Accepted.

Matz.

#5 [ruby-core:78324] Updated by herwinw (Herwin Quarantainenet) over 1 year ago

I can't say the usage of bittest? is directly clear to me. Does it test if resulting integer is not equal to 0? And would we have to use it this way?

if (n & 0b10100000).bittest?

I think a name like Integer#binary_and? (maybe shortened to #binand?) would result in cleaner code

if n.binary_and?(0b10100000)

Of course this would require several other implementations as well, for all other binary operators

#6 [ruby-core:78363] Updated by naruse (Yui NARUSE) over 1 year ago

Herwin Quarantainenet wrote:

I can't say the usage of bittest? is directly clear to me. Does it test if resulting integer is not equal to 0? And would we have to use it this way?

if (n & 0b10100000).bittest?

Like

 if n.bittest?(0b10100000)

I think a name like Integer#binary_and? (maybe shortened to #binand?) would result in cleaner code

if n.binary_and?(0b10100000)

Of course this would require several other implementations as well, for all other binary operators

There's two AND, bitwise and logical.
Therefore it can be bit_and?, but there's no reason to write logical AND as a method, which can be written with &&.

#7 [ruby-core:78376] Updated by herwin (Herwin W) over 1 year ago

if n.bittest?(0b10100000)

If I encountered that code without having the context of this case, I wouldn't know what what the equivalent behaviour would be:

if n & 0b10100000 != 0           #=> Is at least one bit of the argument set?
if n & 0b10100000 == 0b10100000  #=> Are all the bits of the argument set?

There's two AND, bitwise and logical.
Therefore it can be bit_and?, but there's no reason to write logical AND as a method, which can be written with &&.

I was actually thinking about the other bitwise/binary operators here, like | and ^

#8 [ruby-core:78449] Updated by naruse (Yui NARUSE) over 1 year ago

Herwin W wrote:

if n.bittest?(0b10100000)

If I encountered that code without having the context of this case, I wouldn't know what what the equivalent behaviour would be:

if n & 0b10100000 != 0           #=> Is at least one bit of the argument set?
if n & 0b10100000 == 0b10100000  #=> Are all the bits of the argument set?

Above one.

There's two AND, bitwise and logical.
Therefore it can be bit_and?, but there's no reason to write logical AND as a method, which can be written with &&.

I was actually thinking about the other bitwise/binary operators here, like | and ^

I can't show a use case of |.
^ is maybe useful but the name is difficult.

#9 [ruby-core:78450] Updated by shugo (Shugo Maeda) over 1 year ago

Yui NARUSE wrote:

Herwin W wrote:

if n.bittest?(0b10100000)

If I encountered that code without having the context of this case, I wouldn't know what what the equivalent behaviour would be:

if n & 0b10100000 != 0           #=> Is at least one bit of the argument set?
if n & 0b10100000 == 0b10100000  #=> Are all the bits of the argument set?

Above one.

IBM InfoSphere and MS FoxPro have BITTEST(), but its second argument is the bit position
to be tested.

http://www.ibm.com/support/knowledgecenter/SSZJPZ_11.3.0/com.ibm.swg.im.iis.ds.basic.doc/topics/r_dsbasic_BITTEST_function.html
https://msdn.microsoft.com/en-us/library/aa977348(v=vs.71).aspx

This behavior seems to fit the name bittest, compared to the proposed one.

#10 [ruby-core:78451] Updated by shugo (Shugo Maeda) over 1 year ago

Shugo Maeda wrote:

IBM InfoSphere and MS FoxPro have BITTEST(), but its second argument is the bit position
to be tested.

http://www.ibm.com/support/knowledgecenter/SSZJPZ_11.3.0/com.ibm.swg.im.iis.ds.basic.doc/topics/r_dsbasic_BITTEST_function.html
https://msdn.microsoft.com/en-us/library/aa977348(v=vs.71).aspx

This behavior seems to fit the name bittest, compared to the proposed one.

I didn't mean to propose this behavior.
I just meant to point out that bittest? may not be suitable for the proposed behavior.

#11 [ruby-core:82819] Updated by tagomoris (Satoshi TAGOMORI) 8 months ago

How about bitmask_test? or bitflag_test?

shugo (Shugo Maeda) wrote:

Shugo Maeda wrote:

IBM InfoSphere and MS FoxPro have BITTEST(), but its second argument is the bit position
to be tested.

http://www.ibm.com/support/knowledgecenter/SSZJPZ_11.3.0/com.ibm.swg.im.iis.ds.basic.doc/topics/r_dsbasic_BITTEST_function.html
https://msdn.microsoft.com/en-us/library/aa977348(v=vs.71).aspx

This behavior seems to fit the name bittest, compared to the proposed one.

I didn't mean to propose this behavior.
I just meant to point out that bittest? may not be suitable for the proposed behavior.

#12 [ruby-core:83375] Updated by akr (Akira Tanaka) 7 months ago

How about Integer#has_allbits?(n), Integer#has_somebits?(n) and Integer#has_nobits?(n) ?

class Integer
  def has_allbits?(n) self & n == n end
  def has_somebits?(n) self & n != 0 end
  def has_nobits?(n) self & n == 0 end
end

#13 [ruby-core:83378] Updated by matz (Yukihiro Matsumoto) 7 months ago

has_* is not acceptable. It's not compatible with other method names.
I vote for allbit?, anybit? and nobit?. I am not sure about plurality though.

Matz.

#14 [ruby-core:83379] Updated by knu (Akinori MUSHA) 7 months ago

Speaking of plurality, what about:

a.bit?(b)a & b != 0
a.bits?(b)a & b == b

#15 [ruby-core:83391] Updated by phluid61 (Matthew Kerwin) 7 months ago

I think plural makes most sense:

a.allbits? b #→ a & b == b
a.anybits? b #→ a & b != 0
a.nobits? b  #→ a & b == 0

It introduces a strange paradox, though:

a.allbits? 0 #→ true
a.nobits? 0  #→ true

#16 [ruby-core:84017] Updated by aycabta (aycabta .) 6 months ago

phluid61 (Matthew Kerwin) wrote:

It introduces a strange paradox, though:

a.allbits? 0 #→ true
a.nobits? 0  #→ true

I discussed it with watson1978 (Shizuo Fujita) (Shizuo Fujita). We guess the behavior is not strange.

The allbits? means "The receiver checks that all standing bits of the argument don't sit on itself".

a.allbits? 0 #→ true

In this case, "all standing bits of the argument don't sit on the receiver " because "all standing bits of argument" is nothing. So it returns true. I think this is correct. If I have to choose a word, it's reasonable specification.

a.nobits? 0  #→ true

I think this is correct in the same way.

#17 [ruby-core:84033] Updated by naruse (Yui NARUSE) 6 months ago

  • Target version set to 2.5
  • Assignee set to matz (Yukihiro Matsumoto)
  • Status changed from Open to Assigned

A patch is as follows:

diff --git a/numeric.c b/numeric.c
index 1858113c09..511155a3ac 100644
--- a/numeric.c
+++ b/numeric.c
@@ -3209,6 +3209,45 @@ int_even_p(VALUE num)
     return Qfalse;
 }

+/*
+ *  call-seq:
+ *     int.allbits?(mask)  ->  true or false
+ *
+ *  Returns +true+ if all bits of <code>+int+ & +mask+</code> is 1.
+ */
+
+static VALUE
+int_allbits_p(VALUE num, VALUE mask)
+{
+    return rb_int_equal(rb_int_and(num, mask), mask);
+}
+
+/*
+ *  call-seq:
+ *     int.anybits?(mask)  ->  true or false
+ *
+ *  Returns +true+ if any bits of <code>+int+ & +mask+</code> is 1.
+ */
+
+static VALUE
+int_anybits_p(VALUE num, VALUE mask)
+{
+    return num_zero_p(rb_int_and(num, mask)) ? Qfalse : Qtrue;
+}
+
+/*
+ *  call-seq:
+ *     int.nobits?(mask)  ->  true or false
+ *
+ *  Returns +true+ if no bits of <code>+int+ & +mask+</code> is 1.
+ */
+
+static VALUE
+int_nobits_p(VALUE num, VALUE mask)
+{
+    return num_zero_p(rb_int_and(num, mask));
+}
+
 /*
  *  Document-method: Integer#succ
  *  Document-method: Integer#next
@@ -5396,6 +5435,9 @@ Init_Numeric(void)
     rb_define_method(rb_cInteger, "integer?", int_int_p, 0);
     rb_define_method(rb_cInteger, "odd?", int_odd_p, 0);
     rb_define_method(rb_cInteger, "even?", int_even_p, 0);
+    rb_define_method(rb_cInteger, "allbits?", int_allbits_p, 1);
+    rb_define_method(rb_cInteger, "anybits?", int_anybits_p, 1);
+    rb_define_method(rb_cInteger, "nobits?", int_nobits_p, 1);
     rb_define_method(rb_cInteger, "upto", int_upto, 1);
     rb_define_method(rb_cInteger, "downto", int_downto, 1);
     rb_define_method(rb_cInteger, "times", int_dotimes, 0);
diff --git a/test/ruby/test_integer_comb.rb b/test/ruby/test_integer_comb.rb
index 80d08cac04..1ad13dd31b 100644
--- a/test/ruby/test_integer_comb.rb
+++ b/test/ruby/test_integer_comb.rb
@@ -457,6 +457,30 @@ def test_even_odd
     }
   end

+  def test_allbits_p
+    VS.each {|a|
+      VS.each {|b|
+        assert_equal((a & b) == b, a.allbits?(b), "(#{a}).allbits?(#{b}")
+      }
+    }
+  end
+
+  def test_anybits_p
+    VS.each {|a|
+      VS.each {|b|
+        assert_equal((a & b) != 0, a.anybits?(b), "(#{a}).anybits?(#{b}")
+      }
+    }
+  end
+
+  def test_nobits_p
+    VS.each {|a|
+      VS.each {|b|
+        assert_equal((a & b) == 0, a.nobits?(b), "(#{a}).nobits?(#{b}")
+      }
+    }
+  end
+
   def test_to_s
     2.upto(36) {|radix|
       VS.each {|a|

#18 [ruby-core:84039] Updated by aycabta (aycabta .) 6 months ago

Hi naruse (Yui NARUSE), matz said below.

matz (Yukihiro Matsumoto) wrote:

I vote for allbit?, anybit? and nobit?. I am not sure about plurality though.

For your action, as you know, Dir.exists? and File.exists? are deprecated. What do you think about this?

#19 [ruby-core:84042] Updated by phluid61 (Matthew Kerwin) 6 months ago

aycabta (ayca bta) wrote:

Hi naruse (Yui NARUSE), matz said below.

matz (Yukihiro Matsumoto) wrote:

I vote for allbit?, anybit? and nobit?. I am not sure about plurality though.

For your action, as you know, Dir.exists? and File.exists? are deprecated. What do you think about this?

exist/exists is tense, bit/bits is plurality, so it is unrelated.

#20 [ruby-core:84045] Updated by aycabta (aycabta .) 6 months ago

phluid61 (Matthew Kerwin) wrote:

exist/exists is tense, bit/bits is plurality, so it is unrelated.

Oh, thank you...I understand.

#21 [ruby-core:84111] Updated by aycabta (aycabta .) 6 months ago

In Ruby 2.5, Ripper::Lexer::State is introduced:
https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/60945/entry/ext/ripper/lib/ripper/lexer.rb#L49

It is for lex_state of parse.y, and has #& and #| for bit operations with lex_state_bits:
https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/60945/entry/parse.y#L78

RDoc uses it:
https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/60945/entry/lib/rdoc/parser/ripper_state_lex.rb#L321

If Integer#allbit? is implemented at 2.5, it's good for Ripper::Lexer::State and I'll use it for RDoc on 2.5.

#22 Updated by naruse (Yui NARUSE) 6 months ago

  • Status changed from Assigned to Closed

Applied in changeset trunk|r61147.


Integer#allbits?, Integer#anybits?, Integer#nobits? [Feature #12753]

#23 [ruby-core:84314] Updated by marcandre (Marc-Andre Lafortune) 5 months ago

When writing specs, I discovered that coercion was failing for allbits.

I modified all three methods to apply coercion with to_int if needed.

Also available in: Atom PDF