A user enters the string '67.99'. I need to ultimately convert this into the integer 6799.
In other words: convert the currency amount entered via a string into cents via integer data type.
I notice that this happens:
('67.99'.to_f * 100).to_i
#=> 6798
Not expected behavior. I need to save it as 6799, not 6798.
The issue is multiplying this float number by 100:
'67.99'.to_f * 100
#=> 6798.999999999999
Question: How can I properly convert a decimal, entered as a string, into an integer?
Example input and output:
'67' #=> 6700
'67.' #=> 6700
'67.9' #=> 6790
'67.99' #=> 6799
IMO: this is not a duplicate of this question because I am aware that float is not broken.
Use round:
('67.99'.to_f * 100).round
#=> 6799
As discussed in comments, there is a potentially better way to deal with such strings - BigDecimal class:
(BigDecimal.new('67.99') * 100).round
#=> 6799
This becomes relevant for large numbers:
input = '1000000000000001'
(input.to_f * 100).round
#=> 100000000000000096
(BigDecimal.new(input) * 100).round
#=> 100000000000000100
Related
How do I convert a string with a dollar amount such as "5.32" or "100" to an integer amount in cents such as 532 or 10000?
I have a solution below:
dollar_amount_string = "5.32"
dollar_amount_bigdecimal = BigDecimal.new(dollar_amount_string)
cents_amount_bigdecimal = dollar_amount_bigdecimal * BigDecimal.new(100)
cents_amount_int = cents_amount_bigdecimal.to_i
but it seems wonky. I want to be sure because this will be an input to the PayPal API.
I've also tried the money gem, but it wasn't able to take strings as inputs.
You can use String#to_r ("to rational") to avoid round-off error.
def dollars_to_cents(dollars)
(100 * dollars.to_r).to_i
end
dollars_to_cents("12")
#=> 1200
dollars_to_cents("10.25")
#=> 1025
dollars_to_cents("-10.25")
#=> -1025
dollars_to_cents("-0")
#=> 0
d, c = dollar_amount_string.split(".")
d.to_i * 100 + c.to_i # => 532
I started with the original accepted answer, but had to make some important fixes along the way:
def dollars_to_cents(string=nil)
# remove all the signs and formatting
nums = string.to_s.strip.delete("$ CAD ,")
# add CENTS if they do not exit
nums = nums + ".00" unless nums.include?(".")
return (100 * nums.strip.to_r).to_i
end
So far works with these inputs:
CAD 1,215.92
CAD 1230.00
$11123.23
$123
43234.87
43,234.87
When I type
digicollect=[]
digicollect[0]=2
I get 2 when I type in digicollect in the irb.
Also, when I type in
"Hello" * 2
I get "HelloHello"
But if I type in
2 * "Hello"
it doesn't work.
"hello" * digicollect
doesn't work.
but
"hello" * digicollect[0]
does work.
Why?
Everything in ruby is an object, and even multiplications are just method calls.
"Hello" * 2 is the same as "Hello".*(2)
So when you get an error you should ask yourself: Do the left hand side really have the multiplication method and will it accept the right hand side as an argument?
digicollect = []
digicollect[0] = 2
Let us check what kind of objects we have:
p digicollect.class #=> Array
p digicollect[0].class #=> Fixnum
p 2.class #=> Fixnum
p "Hello".class #=> String
Now if we go into the docs for the *-method we find what each class expect:
http://ruby-doc.org/core-2.1.0/String.html#method-i-2A
http://ruby-doc.org/core-2.1.0/Array.html#method-i-2A
http://ruby-doc.org/core-2.1.0/Fixnum.html#method-i-2A
In there we find what will happen:
String expects an Integer. (num of times to repeat string)
Array expects an Integer or a String. (to repeat array x times or to join using string)
Fixnum expects an Numeric. (For simple multiplication)
Thus when you write "hello" * digicollect you are trying to call the multiplication method of a sting and pass it an Array, and the method simply do not know how to handle it (it will only accept Integers), that is why you get the error.
I should preface this by saying I don't get 2 when I type digicollect in irb ... I get [2]. This is a single element array with a value of 2. That's very different from the integer value 2.
String has no * operator for an array argument, and number has no * operator with a string argument. However, String does have * with a number argument, and digicollect[0] access the numeric value of that array element.
digicollect itself is not numeric, that's why you can't 'multiply' by it. It contains numbers, though, which is why "hello" * digicollect[0] works.
As for the 2 * "Hello" case, I believe that's a syntactic thing about the language - The string must come first and the integer second.
I have a string "1/16" I want to convert it to float and multiply it by 45. However, I dont get the desired results.
I am trying in script/console
>> "1/16".to_f
=> 1.0
>> "1/16".to_f*45
=> 45.0
how can i get the desired result of 2.81
Bigger picture:
I have a drop down like this:
<%=select_tag :volume, options_for_select(["", "1 g", "1/16 oz", "1/8 oz","1/4 oz",
"1/2 oz", "1 oz", "1/8 lb", "1/4 lb", "Single", "Multi 5" ], "N/A") %>
whenever user selects oz value then i want to multiply it to 45
so i do:
first, *rest = params[:volume].to_s.split(/ /)
if rest.first=="oz"
#indprodprice = #prods.orig_price.to_i*first.to_f*28.3495
else
#indprodprice = #prods.orig_price.to_i*first.to_f*453.59237
end
Use Rational
>> (Rational(*("1/16".split('/').map( &:to_i )))*45).to_f
=> 2.8125
Looks like you're going to have to parse the fraction yourself. This will work on fractions and whole numbers, but not mixed numbers (ie: 1½ will not work.)
class String
def to_frac
numerator, denominator = split('/').map(&:to_f)
denominator ||= 1
numerator/denominator
end
end
"1/16".to_frac * 45
#Farrel was right, and since Ruby 1.9 includes Rational and String has a to_r-method things are easier:
puts ("1/16".to_r * 45).to_f #=> 2.8125
puts ("1/16".to_r * 45).to_f.round(2) #=> 2.81
In 2.0 it became even easier with a rational literal:
1/16r # => (1/16)
Is there any worthy Ruby method to count the number of digits in a float? Also, how do I specify the precise when to_s float numbers?
# Number of digits
12345.23.to_s.split("").size -1 #=> 7
# The precious part
("." + 12345.23.to_s.split(".")[1]).to_f #=> .023
# I would rather used
# 12345.23 - 12345.23.to_i
# but this gives 0.22999999999563
to specify precision of a float in Ruby. you can use the round method.
number.round(2)
2 is the precision.
53.819.round(2) -> 53.82
I think you should check out the number_with_precision helper.
number_with_precision(13, :precision => 5) # => 13.00000
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}