I want find the length
of a Fixnum
, num
, without converting it into a String
.
In other words, how many digits are in num
without calling the .to_s()
method:
num.to_s.length
I want find the length
of a Fixnum
, num
, without converting it into a String
.
In other words, how many digits are in num
without calling the .to_s()
method:
num.to_s.length
puts Math.log10(1234).to_i + 1 # => 4
You could add it to Fixnum like this:
class Fixnum
def num_digits
Math.log10(self).to_i + 1
end
end
puts 1234.num_digits # => 4
n.to_s.length
is faster for any integer represented by a Fixnum. A lot faster: On my box, n.to_s.length
takes somewhere between a third and half the time of the logarithm method, depending upon the length of the number. If the number has to be represented a Bignum, then the logarithm method starts winning. Both methods are very fast, though, at around .6 milliseconds (for the logarithm method), and between 0.2 and 0.3 milliseconds (for the string method). –
Fungous Math.log10
must have a rather inefficient implementation. I just tried a simple method which walks over a table of all the powers of 10 which fit in 32/64 bits, and does a >=
comparison for each one -- it was a touch faster than Math.log10
, but still slower than to_s
. It could be made faster by "unrolling" a binary search of the same table, just like unrolling a loop (then the table wouldn't be needed any more -- the same numbers would be hard-coded into a series of conditionals). –
Tanganyika .abs
resulted in a warning (when run with ruby -w
) which I did not understood nor cared for. I figured an error was still better then a wrong result from the to_s.size
idea. –
Irrigate Ruby 2.4 has an Integer#digits method, which return an Array containing the digits.
num = 123456
num.digits
# => [6, 5, 4, 3, 2, 1]
num.digits.count
# => 6
EDIT:
To handle negative numbers (thanks @MatzFan), use the absolute value. Integer#abs
-123456.abs.digits
# => [6, 5, 4, 3, 2, 1]
Math::DomainError
. So much more Ruby though –
Excision .digits
for large scale use cases –
Whitethroat Sidenote for Ruby 2.4+
I ran some benchmarks on the different solutions, and Math.log10(x).to_i + 1
is actually a lot faster than x.to_s.length
. The comment from @Wayne Conrad is out of date. The new solution with digits.count
is trailing far behind, especially with larger numbers:
with_10_digits = 2_040_240_420
print Benchmark.measure { 1_000_000.times { Math.log10(with_10_digits).to_i + 1 } }
# => 0.100000 0.000000 0.100000 ( 0.109846)
print Benchmark.measure { 1_000_000.times { with_10_digits.to_s.length } }
# => 0.360000 0.000000 0.360000 ( 0.362604)
print Benchmark.measure { 1_000_000.times { with_10_digits.digits.count } }
# => 0.690000 0.020000 0.710000 ( 0.717554)
with_42_digits = 750_325_442_042_020_572_057_420_745_037_450_237_570_322
print Benchmark.measure { 1_000_000.times { Math.log10(with_42_digits).to_i + 1 } }
# => 0.140000 0.000000 0.140000 ( 0.142757)
print Benchmark.measure { 1_000_000.times { with_42_digits.to_s.length } }
# => 1.180000 0.000000 1.180000 ( 1.186603)
print Benchmark.measure { 1_000_000.times { with_42_digits.digits.count } }
# => 8.480000 0.040000 8.520000 ( 8.577174)
x
, Math.log10(x).to_i + 1
runs in 0.000013s on my machine; x.to_s.length
takes 45.9s (and I'm not willing to try x.digits.count
). –
Rattle Although the top-voted loop is nice, it isn't very Ruby and will be slow for large numbers, the .to_s is a built-in function and therefore will be much faster. ALMOST universally built-in functions will be far faster than constructed loops or iterators.
Another way:
def ndigits(n)
n=n.abs
(1..1.0/0).each { |i| return i if (n /= 10).zero? }
end
ndigits(1234) # => 4
ndigits(0) # => 1
ndigits(-123) # => 3
If you don't want to use regex, you can use this method:
def self.is_number(string_to_test)
is_number = false
# use to_f to handle float value and to_i for int
string_to_compare = string_to_test.to_i.to_s
string_to_compare_handle_end = string_to_test.to_i
# string has to be the same
if(string_to_compare == string_to_test)
is_number = true
end
# length for fixnum in ruby
size = Math.log10(string_to_compare_handle_end).to_i + 1
# size has to be the same
if(size != string_to_test.length)
is_number = false
end
is_number
end
You don't have to get fancy, you could do as simple as this.
def l(input)
output = 1
while input - (10**output) > 0
output += 1
end
return output
end
puts l(456)
It can be a solution to find out the length/count/size of a fixnum.
irb(main):004:0> x = 2021
=> 2021
irb(main):005:0> puts x.to_s.length
4
=> nil
irb(main):006:0>
© 2022 - 2024 — McMap. All rights reserved.
Fixnum
"? In what representation? – Lehrer