[Feature #20163] Add Integer#bit_count

This commit is contained in:
John Hawthorn committed 2026-07-10 11:55:58 -07:00
1 parent 0b2b21767b
commit 23c056c83c
6 files changed
+129
Notes: git 2026-07-10 18:56:35 +00:00

No files matched your search

+17
View File
@@ -7029,6 +7029,23 @@ rb_big_bit_length(VALUE big)
INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER);
}
VALUE
rb_big_bit_count(VALUE big)
{
if (BIGNUM_NEGATIVE_P(big))
rb_raise(rb_eArgError, "bit_count is undefined for negative integers");
BDIGIT *ds = BDIGITS(big);
size_t n = BIGNUM_LEN(big);
size_t count = 0;
while (n--) {
count += rb_popcount64((uint64_t)ds[n]);
}
return SIZET2NUM(count);
}
VALUE
rb_big_odd_p(VALUE num)
{
+1
View File
@@ -123,6 +123,7 @@ VALUE rb_big_aref2(VALUE num, VALUE beg, VALUE len);
VALUE rb_big_abs(VALUE x);
VALUE rb_big_size_m(VALUE big);
VALUE rb_big_bit_length(VALUE big);
VALUE rb_big_bit_count(VALUE big);
VALUE rb_big_remainder(VALUE x, VALUE y);
VALUE rb_big_gt(VALUE x, VALUE y);
VALUE rb_big_ge(VALUE x, VALUE y);
+1
View File
@@ -126,6 +126,7 @@ VALUE rb_int_even_p(VALUE num);
VALUE rb_int_odd_p(VALUE num);
VALUE rb_int_abs(VALUE num);
VALUE rb_int_bit_length(VALUE num);
VALUE rb_int_bit_count(VALUE num);
VALUE rb_int_uminus(VALUE num);
VALUE rb_int_comp(VALUE num);
+44
View File
@@ -5732,6 +5732,49 @@ rb_int_bit_length(VALUE num)
return Qnil;
}
static VALUE
rb_fix_bit_count(VALUE fix)
{
long v = FIX2LONG(fix);
if (v < 0)
rb_raise(rb_eArgError, "bit_count is undefined for negative integers");
return LONG2FIX(rb_popcount_intptr((uintptr_t)v));
}
/*
* call-seq:
* bit_count -> integer
*
* Returns the number of set bits (bits equal to 1) in the binary
* representation of +self+, also known as the population count or
* Hamming weight.
*
* 0.bit_count # => 0
* 1.bit_count # => 1
* 7.bit_count # => 3
* 0b10101.bit_count # => 3
* 255.bit_count # => 8
* (2**1000).bit_count # => 1
* (2**1000-1).bit_count # => 1000
*
* Raises an exception if +self+ is negative.
*
* (-1).bit_count # Raises ArgumentError
*
*/
VALUE
rb_int_bit_count(VALUE num)
{
if (FIXNUM_P(num)) {
return rb_fix_bit_count(num);
}
else if (RB_BIGNUM_TYPE_P(num)) {
return rb_big_bit_count(num);
}
UNREACHABLE_RETURN(Qnil);
}
static VALUE
rb_fix_digits(VALUE fix, long base)
{
@@ -6610,6 +6653,7 @@ Init_Numeric(void)
rb_define_method(rb_cInteger, ">>", rb_int_rshift, 1);
rb_define_method(rb_cInteger, "digits", rb_int_digits, -1);
rb_define_method(rb_cInteger, "bit_count", rb_int_bit_count, 0);
#define fix_to_s_static(n) do { \
VALUE lit = rb_fstring_literal(#n); \
+31
View File
@@ -0,0 +1,31 @@
require_relative '../../spec_helper'
ruby_version_is "4.1" do
describe "Integer#bit_count" do
it "returns the number of set bits in the binary representation" do
0.bit_count.should == 0
1.bit_count.should == 1
2.bit_count.should == 1
3.bit_count.should == 2
7.bit_count.should == 3
0b10101.bit_count.should == 3
0xff.bit_count.should == 8
0x100.bit_count.should == 1
fixnum_max.bit_count.should == fixnum_max.to_s(2).count("1")
(2**1000).bit_count.should == 1
(2**1000 - 1).bit_count.should == 1000
(2**1000 + 2**500).bit_count.should == 2
(2**64 - 1).bit_count.should == 64
(2**10000 - 1).bit_count.should == 10000
end
it "raises an ArgumentError for a negative number" do
-> { -1.bit_count }.should raise_error(ArgumentError)
-> { -19.bit_count }.should raise_error(ArgumentError)
-> { fixnum_min.bit_count }.should raise_error(ArgumentError)
-> { (-2**1000).bit_count }.should raise_error(ArgumentError)
-> { (-2**1000 - 1).bit_count }.should raise_error(ArgumentError)
end
end
end
+35
View File
@@ -632,6 +632,41 @@ class TestInteger < Test::Unit::TestCase
}
end
def test_bit_count
assert_equal(0, 0.bit_count)
assert_equal(1, 1.bit_count)
assert_equal(1, 2.bit_count)
assert_equal(2, 3.bit_count)
assert_equal(3, 7.bit_count)
assert_equal(3, 0b10101.bit_count)
assert_equal(8, 0xff.bit_count)
assert_equal(1, 0x100.bit_count)
# Fixnum boundaries
assert_equal(1, (2**30).bit_count)
assert_equal(1, (2**62).bit_count)
# Bignum
assert_equal(1, (2**64).bit_count)
assert_equal(64, (2**64-1).bit_count)
assert_equal(1, (2**1000).bit_count)
assert_equal(1000, (2**1000-1).bit_count)
assert_equal(2, (2**1000 + 2**500).bit_count)
0.upto(1000) {|i|
n = 2**i
assert_equal(1, n.bit_count, "(#{n}).bit_count")
assert_equal(i, (n-1).bit_count, "(#{n-1}).bit_count")
assert_equal(2, (n+1).bit_count, "(#{n+1}).bit_count") if i >= 1
}
end
def test_bit_count_for_negative_numbers
assert_raise(ArgumentError) { -1.bit_count }
assert_raise(ArgumentError) { -19.bit_count }
assert_raise(ArgumentError) { (-2**1000).bit_count }
end
def test_digits
assert_equal([0], 0.digits)
assert_equal([1], 1.digits)