Ruby round float to_int if whole number - ruby

In ruby, I want to convert a float to an int if it's a whole number. For example
a = 1.0
b = 2.5
a.to_int_if_whole # => 1
b.to_int_if_whole # => 2.5
Basically I'm trying to avoid displaying ".0" on any number that doesn't have a decimal. I'm looking for an elegant (or built-in) way to do
def to_int_if_whole(float)
(float % 1 == 0) ? float.to_i : float
end

One simple way to it would be:
class Float
def prettify
to_i == self ? to_i : self
end
end
That's because:
irb> 1.0 == 1
=> true
irb> 1 == 1.0
=> true
Then you could do:
irb> 1.0.prettify
=> 1
irb> 1.5.prettify
=> 1.5

A one liner sprintf...
sprintf("%g", 5.0)
=> "5"
sprintf("%g", 5.5)
=> "5.5"

This is the solution that ended up working the way I want it to:
class Float
alias_method(:original_to_s, :to_s) unless method_defined?(:original_to_s)
def is_whole?
self % 1 == 0
end
def to_s
self.is_whole? ? self.to_i.to_s : self.original_to_s
end
end
This way I can update the is_whole? logic (I seems like tadman's is the most sophisticated) if needed, and it ensures that anywhere a Float outputs to a string (eg, in a form) it appears the way I want it to (ie, no zeros on the end).
Thanks to everybody for your ideas - they really helped.

I'm don't know much about Ruby.
But this is a display issue. I would be extremely surprised if the libraries you are using don't have a way to format a number when you convert it to a string.
There might not be a catch-all formatting option that does exactly what you want but you could set up a method that returns true if the float is the float representation of a whole number and false otherwise. Inside a formatting routine that you create (so you only have to do this in once place) just change the formatting based on if this is true or false.
This discusses how to control the number of digits that appear after the decimal when displaying a number.
Watch out for the intricacies of floating point representations. Math might say the answer is 3 but you may get 3.000000000000000000001. I'd suggest using a delta to see if the number is almost an integer number.

If you are using rails you can use helper number_to_rounded with option strip_insignificant_zeros, for example:
ActiveSupport::NumberHelper.number_to_rounded(42.0, strip_insignificant_zeros: true)
or like this:
42.0.to_s(:rounded, strip_insignificant_zeros: true)

Although I'd tend to agree with the above post, if you must do this:
(float == float.floor) ? float.to_i : float

Here's my horribly hacktastic implementation provided for educational purposes:
class Float
def to_int_if_whole(precision = 2)
("%.#{precision}f" % self).split(/\./).last == '0' * precision and self.to_i or self
end
end
puts 1.0.to_int_if_whole # => 1
puts 2.5.to_int_if_whole # => 2.5
puts 1.9999999999999999999923.to_int_if_whole # => 2
The reason for using the sprintf-style call is that it handles floating point approximations much more reliably than the Float#round method tends to.

I don't know much about Ruby either.
But in C++, I'd do this:
bool IsWholeNumber( float f )
{
const float delta = 0.0001;
int i = (int) f;
return (f - (float)i) < delta;
}
And then I'd format the output precision based on that.

Related

math expression Randomly rounding up

In the code below 2/5 results to 0.0 instead of 0.4 which should be correct answer
require 'rubygems'
require 'dentaku'
expression = '2/5'
calculator = Dentaku::Calculator.new
result = calculator.evaluate(expression).to_f
puts "#{result}"
I am using dentaku to evaluate the the math expression the documentation for this gem can be found here: https://github.com/rubysolo/dentaku/blob/master/README.md
Your first stop when searching for an answer to this should have been the official documentation for /:
fix / numeric → numeric_result
Performs division: the class of the resulting object depends on the class of numeric and on the magnitude of the result. It may return a Bignum.
For example:
3/2 # => 1
(3/2).class # => Fixnum
3/2.0 # => 1.5
(3/2.0).class # => Float
Ruby will return a Float if either value is a Float:
3.0/2 # => 1.5
(3.0/2).class # => Float
As far as I can tell, Dentaku treats division of two integers as integer division. Try 2/5.0.
Please try expression = '2.0/5.0' instead, maybe your numbers are considered as integers (you want floats) because of the missing ".0" and so you get an integer result of zero

Ruby division infinity/NaN should return 0

I am having an application on Ruby On Rails.
In application I want to override parent class of division in Ruby.
For handling below exceptions.
I Googled everywhere. I want to override ruby division method in application.
So that for below results it should return zero.
0.0 / 0
=> NaN
1.0 / 0
=> Infinity
ZeroDivisionError: divided by 0
I can handle it by changing code everywhere while divide operation. But I want to save my time by overriding the method itself.
You do not need a special method or to extend the float class as other answers state.
Ruby provides you a method on the Float class for this called .finite?
http://ruby-doc.org/core-1.9.3/Float.html#method-i-finite-3F
finite? → true or false
Returns true if flt is a valid IEEE floating point number (it is not infinite, and nan? is false).
if value.finite? == false
value = 0
else
value = value
end
The above example is a bit verbose.
Very similar to: How can I redefine Fixnum's + (plus) method in Ruby and keep original + functionality?
class Float
alias_method :old_div, :/
def /(y)
return NAN if self == y && y == 0.0
return INFINITY if self == 1.0 && y == 0.0
self.old_div(y)
end
end
I know the code above might not be what you exactly want. Feel free to customize it the way you want =)
Overriding the division of Fixnum, Decimal, etc. is possible, but might not be the best solution for you. You would need to override methods in several classes, and they might have some very nasty side-effects (remember - these methods are not called only from your code!!)
I would suggest you write some helper module, which will implement this new behavior, and that you would call it instead of /:
module WeirdMath
self.div(n1, n2)
result = n1 / n2
result.nan? || result.infinite? ? 0 : result
rescue
0
end
end
WeirdMath.div(0.0, 0) # => 0
WeirdMath.div(1.0, 0) # => 0
WeirdMath.div(3.0, 2) # => 1.5

How do I round a float to a specified number of significant digits in Ruby?

It would be nice to have an equivalent of R's signif function in Ruby.
For example:
>> (11.11).signif(1)
10
>> (22.22).signif(2)
22
>> (3.333).signif(2)
3.3
>> (4.4).signif(3)
4.4 # It's usually 4.40 but that's OK. R does not print the trailing 0's
# because it returns the float data type. For Ruby we want the same.
>> (5.55).signif(2)
5.6
There is probably better way, but this seems to work fine:
class Float
def signif(signs)
Float("%.#{signs}g" % self)
end
end
(1.123).signif(2) # => 1.1
(11.23).signif(2) # => 11.0
(11.23).signif(1) # => 10.0
Here's an implementation that doesn't use strings or other libraries.
class Float
def signif(digits)
return 0 if self.zero?
self.round(-(Math.log10(self).ceil - digits))
end
end
I don't see anything like that in Float. Float is mostly a wrapper for the native double type and given the usual binary/decimal issues, I'm not that surprised that Float doesn't allow you to manipulate the significant digits.
However, BigDecimal in the standard library does understand significant digits but again, I don't see anything that allows you to directly alter the significant digits in a BigDecimal: you can ask for it but you can't change it. But, you can kludge around that by using a no-op version of the mult or add methods:
require 'bigdecimal'
a = BigDecimal.new('11.2384')
a.mult(1, 2) # the result is 0.11E2 (i.e. 11)
a.add(0, 4) # the result is 0.1124E2 (i.e. 11.24)
The second argument to these methods:
If specified and less than the number of significant digits of the result, the result is rounded to that number of digits, according to BigDecimal.mode.
Using BigDecimal will be slower but it might be your only choice if you need fine grained control or if you need to avoid the usual floating point problems.
Some of the previous answers and comments have alluded to this solution but this is what worked for me:
# takes in a float value and returns another float value rounded to
# given significant figures.
def round_to_sig_figs(val, sig_figs)
BigDecimal.new(val, sig_figs).to_f
end
You are probably looking for Ruby's Decimal.
You could then write:
require 'decimal/shortcut'
num = 1.23541764
D.context.precision = 2
num_with_2_significant_digits = +D(num.to_s) # => Decimal('1.2')
num_with_2_significant_digits.to_f # => 1.2000000000000002
Or if you prefer to use the same syntax add this as a function to class Float like this:
class Float
def signif num_digits
require 'decimal/shortcut'
D.context.precision = num_digits
(+D(self.to_s)).to_f
end
end
Usage would then be the same, i.e.
(1.23333).signif 3
# => 1.23
To use it, install the gem
gem install ruby-decimal
#Blou91's answer is nearly there, but it returns a string, instead of a float. This below works for me:
(sprintf "%.2f", 1.23456).to_f
So as a function,
def round(val, sig_figs)
(sprintf "%.#{sig_figs}f", val).to_f
end
Use sprintf if you want to print trailing zeros
2.0.0-p353 :001 > sprintf "%.3f", 500
=> "500.000"
2.0.0-p353 :002 > sprintf "%.4f", 500
=> "500.0000"
2.0.0-p353 :003 >

Why does Ruby give this large precision decimal result?

I want to convert a subtitle time code:
begin="00:00:07.71" dur="00:00:03.67
to pure seconds:
begin=7.1 end=11.38
I wrote a Ruby code:
def to_sec(value)
a = value.split(':')
a[0].to_i*3600+a[1].to_i*60+a[2].to_f
end
which resulted in 11.379999999999999.
Can anybody tell me why this happens?
Is there any Time library that can do this conversion?
It'll probably be easiest for you to represent your underlying datatype as integer hundredths of a second (centiseconds):
def to_csec(value) #if you had CSec < Integer this would be `def self.[](value)`
a = value.split(':')
#tacking on a couple zeros to each
a[0].to_i*360000+a[1].to_i*6000+(a[2].to_f * 100).to_i
end
You could add some helpers for dealing with the durations and pretty printing them as well:
def csec_to_s(csec) #if you had CSec < Integer, this would be `def to_sec`
"%.2f" % (csec.to_f / 100)
end
class SubtitleDuration < Range
def initialize(a,b)
centi_a = to_csec(a)
super(centi_a,to_csec(b) + centi_a)
end
def to_s
"begin=#{csec_to_s(self.begin)} end=#{csec_to_s(self.end) }"
end
end
Then your answer is just:
puts SubtitleDuration.new("00:00:07.71", "00:00:03.67").to_s
#=> begin=7.71 end=11.38
This sort of thing can happen in just about any programming language. It's because of how floating point numbers are represented. They're not stored as decimals under the hood, so sometimes you get odd rounding errors like this.

Set the display precision of a float in Ruby

Is it possible to set the display precision of a float in Ruby?
Something like:
z = 1/3
z.to_s #=> 0.33333333333333
z.to_s(3) #=> 0.333
z.to_s(5) #=> 0.33333
Or do I have to override the to_s method of Float?
z.round(2) or x.round(3) is the simplest solution. See http://www.ruby-doc.org/core-1.9.3/Float.html#method-i-round.
That said, that will only ensure that it is no more than that many digits. In the case of 1/3 that is fine, but if you had say 0.25.round(3) you will get 0.25, not 0.250.
You can use sprintf:
sprintf("%0.02f", 123.4564564)
I would normally just do the conversion in open code, something like:
puts "%5.2f" % [1.0/3.0]
Ruby calls Kernel#format for expressions like this, because String has a core operator % defined on it. Think of it as printf for Ruby if that rings any bells for you.
Rubocop recommends using #format over #sprintf and using annotated string tokens.
The syntax for #format is
%[flags][width][.precision]type
Example:
# Ensure we store z as a float by making one of the numbers a float.
z = 1/3.0
# Format the float to a precision of three.
format('%<num>0.3f', num: z)
# => "0.333"
format('%<num>0.5f', num: z)
# => "0.33333"
# Add some text to the formatted string
format('I have $%<num>0.2f in my bank account.', num: z)
# => "I have $0.33 in my bank account."
References:
https://www.rubydoc.info/github/bbatsov/RuboCop/RuboCop/Cop/Style/FormatString
https://www.rubydoc.info/github/bbatsov/RuboCop/RuboCop/Cop/Style/FormatStringToken
You can use puts
z = #{'%.3f' % z}

Resources