I'm trying to define a function that respects the following rounding conditions (round to the closest integer or tenth):
The main issue I found out, was around rounding negative numbers.
Here is my implementation (sorry for the conditional check but its just for this example):
def convention_round(number, to_int = false)
if to_int
number.round
else
number.round(1)
end
end
convention_round(1.2234) # 1.2
convention_round(1.2234, true) # 1
convention_round(1.896) # 1.9
convention_round(1.896, true) # 2
convention_round(1.5) # 1.5
convention_round(1.5, true) # 2
convention_round(1.55) # 1.6
convention_round(1.55, true) # 2
convention_round(-1.2234) # -1.2
convention_round(-1.2234, true) # -1
convention_round(-1.896) # -1.9
convention_round(-1.2234, true) # -2
convention_round(-1.5) # -1.5
convention_round(-1.5, true) # -2 (Here I want rounded to -1)
convention_round(-1.55) # -1.6 (Here I want rounded to -1.5)
convention_round(-1.55, true) # -2
I'm not 100% sure what is the best approach for rounding the negative numbers.
Thank you!
From the docs, you can use Integer#round (and Float#round) for this, as follows:
def convention_round(number, precision = 0)
number.round(
precision,
half: (number.positive? ? :up : :down)
)
end
convention_round(1.4) #=> 1
convention_round(1.5) #=> 2
convention_round(1.55) #=> 2
convention_round(1.54, 1) #=> 1.5
convention_round(1.55, 1) #=> 1.6
convention_round(-1.4) #=> -1
convention_round(-1.5) #=> -1 # !!!
convention_round(-1.55) #=> -2
convention_round(-1.54, 1) #=> -1.55
convention_round(-1.55, 1) #=> -1.5 # !!!
This isn't quite the method signature you asked for, but it's a more generic form - since you can supply an arbitrary precision.
However, I would like to point out the irony that (despite the method name) this is not a conventional way of rounding numbers.
There are a few different conventions, all(?) of which are supported by the ruby core library (see above link to docs), but this is not one of them.
Related
There must be a simple way to achieve this, I have an DB field containing an integer and I want to reformat it into a float to display.
As an integer my value looks like 6500 and I want it to display as 65.00
Within my model I have attempted to achieve this by creating the following method
def get_payment_amount_as_number
amount = self.payment_amount
return '%.02f' % self.payment_amount.to_f
end
Which results in the following being displayed: 6500.00
What would the best approach be to either strip the initial zeroes or to simply insert a decimal point?
Whilst I imagine this a ruby related question, I am not sure if rails has a handy helper already in place?
Thank you.
You could divide the number by 100:
payment_amount = 6595
'%.02f' % payment_amount.fdiv(100)
#=> "65.95"
'%.02f' % (payment_amount / 100.0)
#=> "65.95"
Or you could convert the number to a string and insert a decimal point:
payment_amount.to_s.rjust(3, '0').insert(-3, '.')
#=> "65.95"
Rails also provides several helpers to format numbers:
number_to_currency(65.95)
#=> "$65.95"
number_to_currency(1000)
#=> "$1,000.00"
And you might want to take a look at the money-rails gem which provides a mapping from cents to money objects.
You do this simply ...
def get_payment_amount_as_number
amount = self.payment_amount / 100
#to convert amount to float
amount.to_f
end
I find another one
amount = self.payment_amount
# => 6500
ans = '%.2f' % (amount/100)
# => "65.00"
int_value = 6500
float_value = float_value = '%.2f' % (int_value / 100.0)
puts int_value: int_value, float_value: float_value
it's all!
I'm dealing with currencies and I want to round down the number to 2 decimal places. Even if the number is 500.0, I would like it to be 500.00 to be consistent. When I do "500.00".to_d it converts it to 500.0.
Whats a good way of changing this behavior? I also use this method to round down to 2 digits and make sure it always has 2 decimals.
def self.round_down(x, n=2)
s = x.to_s
l = s.index('.') ? s.index('.') + 1 + n : s.length
s = s[0, l]
s = s.index('.') ? s.length - (s.index('.') + 1) == 1 ? s << '0' : s : s << '.00'
s.to_f
end
In addition to mcfinnigan's answer, you can also use the following to get 2 decimal places
'%.2f' % 500 # "500.00"
This use case is known as the string format operator
Since you are using Rails and this seems to be related to a view, there's number_with_precision:
number_with_precision(500, precision: 2)
#=> "500.00"
I18n.locale = :de
number_with_precision(500, precision: 2)
#=> "500,00"
For currencies I'd suggest number_to_currency:
number_to_currency(500)
#=> "$500.00"
Here's a hint. 500.00 is a representation of the number 500.0
Specifically, sprintf will help you:
irb(main):004:0> sprintf "%.2f", 500.0
=> "500.00"
Do not use floating point numbers to represent money. See this question for a good overview of why this is a bad idea.
Instead, store monetary values as integers (representing cents), or have a look at the money gem that provides lots of useful functionality for dealing with such values.
There was a requirement to round DOWN.
Most other answers round 500.016 UP to 500.02
Try:
def self.round_down(x, n = 2)
"%.#{n}f" % x.to_d.truncate(n)
end
irb(main):024:0> x=500.0; '%.2f' % x.to_d.truncate(2)
=> "500.00"
irb(main):025:0> x=500.016; '%.2f' % x.to_d.truncate(2)
=> "500.01"
How can i round in ruby only higher, and so, that 2 last numbers are null?
For exapmle
4233.000001 to 4300
52825 to 52900
627444 to 627500
111999 to 112000
?
Now i can round only in mathematic-rules via round(-2), but how to do only higher, and only with 2 null's on the end?
You should use ceil
def my_round a
(a / 100.0).ceil * 100
end
my_round 4233.000001 # => 4300
my_round 52825 # => 52900
my_round 627444 # => 627500
my_round 111999 # => 112000
working off Sergio's answer, you could mix a module in to the actual Numeric object for a more general solution:
module RoundsUp
def round_up(ndigits)
pow_ten = 10 ** -ndigits
(self / pow_ten.to_f).ceil * pow_ten
end
end
then
mynumeric = 262.33
mynumeric.extend(RoundsUp)
mynumeric.round_up(-2) #=> 300
and you've got a method that behaves like the normal round for any number of digits
I created Float class in lib folder:
class Float
def precision(p = 2)
# Make sure the precision level is actually an integer and > 0
raise ArgumentError, "#{p} is an invalid precision level. Valid ranges are integers > 0." unless p.class == Fixnum or p < 0
# Special case for 0 precision so it returns a Fixnum and thus doesn't have a trailing .0
return self.round if p == 0
# Standard case
(self * 10**p).round.to_f / 10**p
end
end
In rspec tests, works. But when the application is running, this error is raised:
undefined method `precision' for 5128.5:Float
How to make this override work?
Ruby already implements a round method for Float. There is no need for your implementation.
0.12345.round(2) # => 0.12
0.12345.round(3) # => 0.123
I think this should do it.
module MyFloatMod
def precision(p = 2)
# Make sure the precision level is actually an integer and > 0
raise ArgumentError, "#{p} is an invalid precision level. Valid ranges are integers > 0." unless p.class == Fixnum or p < 0
# Special case for 0 precision so it returns a Fixnum and thus doesn't have a trailing .0
return self.round if p == 0
# Standard case
(self * 10**p).round.to_f / 10**p
end
end
Float.send(:include, MyFloatMod)
EDIT: Almost forgot you also need to make sure this all gets included somewhere during your app's start up.
I have a calculation that generates what appears to be the Float 22.23, and a literal 22.23 like so:
some_object.total => 22.23
some_object.total.class => Float
22.23 => 22.23
22.23.class => Float
But for some reason, the following is false:
some_object.total == 22.23 ? true : false
Wacky, right?
Is there some kind of precision mechanism being used that maybe isn't completely transparent through the some_object.total call?
Floating-point numbers cannot precisely represent all decimal numbers within their range. For example, 0.9 is not exactly 0.9, it's a number really close to 0.9 that winds up being printed as it in most cases. As you do floating-point calculations, these errors can accumulate and you wind up with something very close to the right number but not exactly equal to it. For example, 0.3 * 3 == 0.9 will return false. This is the case in every computer language you will ever use — it's just how binary floating-point math works. See, for example, this question about Haskell.
To test for floating point equality, you generally want to test whether the number is within some tiny range of the target. So, for example:
def float_equal(a, b)
if a + 0.00001 > b and a - 0.00001 < b
true
else
false
end
end
You can also use the BigDecimal class in Ruby to represent arbitrary decimal numbers.
If this is a test case, you can use assert_in_delta:
def test_some_object_total_is_calculated_correctly
assert_in_delta 22.23, some_object.total, 0.01
end
Float#to_s and Float#inspect round. Try "%.30f" % some_object.total and you will see that it's not quite 22.23.
there is something else going on here. this is from a 1.8.7 irb
irb(main):001:0> class Test
irb(main):002:1> attr_accessor :thing
irb(main):003:1> end
=> nil
irb(main):004:0> t = Test.new
=> #<Test:0x480ab78>
irb(main):005:0> t.thing = 22.5
=> 22.5
irb(main):006:0> t.thing == 22.5
=> true