Float with 'unnecessary' digits - ruby

I need to make a number out of the string. to do that i use well known maneuver looking something like this:
Float(string_var) rescue nil
This works nicely, however I do have a tiny, little problem. If a string is "2.50", variable I get is 2.5. Is it even possible to create Float object with 'unnecessary' 0 digit at the end? can I literally translate "2.50" into 2.50 ?

In short, the answer is no, given the question, as any Float, when examined, will use Float's to_s function, eliciting an answer without trailing zeroes.
Float will always give you a numeric value that can be interpreted any way you wish, though. In your example, you will get a float value (given a string that is a parsable float). What you are asking then, is how to display that value with trailing zeroes. To do that, you will be turning the float value back into a string.
Easiest way to accomplish that is to use the format given by one of your respondents, namely
string_var = "2.50"
float_value = Float(string_var) rescue nil # 2.5
with_trailing_zeroes = "%0.2f" % float_value # '2.50'

Related

How to read percentage as decimal number?

I'm trying to find the decimal value from a percentage that a user inputs.
For example, if a user inputs "15", i will need to do a calculation of 0.15 * number.
I've tried using .to_f, but it returns 15.0:
15.to_f
#=> 15.0
I also tried to add 0. to the beginning of the percentage, but it just returns 0:
15.to_s.rjust(4, "0.").to_i
#=> 0
Divide by 100.0
The easiest way to do what you're trying to do is to divide your input value by a Float (keeping in mind the inherent inaccuracy of floating point values). For example:
percentage = 15
percentage / 100.0
#=> 0.15
One benefit of this approach (among others) is that it can handle fractional percentages just as easily. Consider:
percentage = 15.6
percentage / 100.0
#=> 0.156
If floating point precision isn't sufficient for your use case, then you should consider using Rational or BigDecimal numbers instead of a Float. Your mileage will very much depend on your semantic intent and accuracy requirements.
Caveats
Make sure you have ahold of a valid Integer in the first place. While others might steer you towards String#to_i, a more robust validation is to use Kernel#Integer so that an exception will be raised if the value can't be coerced into a valid Integer. For example:
print "Enter integer: "
percentage = Integer gets
If you enter 15\n then:
percentage.class
#=> Integer
If you enter something that can't be coerced to an Integer, like foo\n, then:
ArgumentError (invalid value for Integer(): "foo\n")
Using String#to_i is much more permissive, and can return 0 when you aren't expecting it, such as when called on nil, an empty string, or alphanumeric values that don't start with an integer. It has other interesting edge cases as well, so it's not always the best option for validating input.
I'm trying to find the amount from a percentage that a user inputs
If you retrieve the input via gets, you typically convert it to a numeric value first, e.g.
percentage = gets.to_i
#=> 15
Ruby is not aware that this 15 is a percentage. And since there's no Percentage class, you have to convert it into one of the existing numeric classes.
15% is equal to the fraction 15/100, the ratio 15:100, or the decimal number 0.15.
If you want the number as a (maybe inexact) Float, you can divide it by 100 via fdiv:
15.fdiv(100)
#=> 0.15
If you prefer a Rational you can use quo: (it might also return an Integer)
15.quo(100)
#=> (3/20)
Or maybe BigDecimal for an arbitrary-precision decimal number:
require 'bigdecimal'
BigDecimal(15) / 100
#=> 0.15e0
BigDecimal also accepts strings, so you could pass the input without prior conversion:
input = gets
BigDecimal(input) / 100
#=> 0.15e0

Does Ruby auto-promote numeric values for comparison?

Why on earth does comparing a float value of 1.0, to an integer value of 1, return true?
puts '1.0'.to_i
puts '1.0'.to_i == 1.0 #so 1 == 1.0 is true?
puts 1.0 == 1 #wtf?
Does Ruby only read the first part of the floatin point value and then short circuit? Wuld someone be able to explain with a link to some documentation please? I have flipped through the API but I don't even know what to look for in this case...
== compares the value, the value of 1.0 is equal to 1 in math, so it's not much surprising. To compare value as well as type, you can use eql?:
1.0 == 1
#=> true
1.0.eql? 1
#=> false
In Ruby, == is a method. That means to understand it you need to look at the specific class calling it.
1 == 1.0
The caller is 1, a Fixnum. So you need to look at Fixnum#==.
1.0 == 1
The caller is 1.0, a Float. So you need to look at Float#==.
A surprising result of this is that == is not necessarily symmetric: a == b and b == a could call completely different methods and return completely different results. In this case though, both == methods end up calling the C function rb_integer_float_eq which converts both operands to the same data type before comparing them.
Actually there already is a nice answer "What's the difference between equal?, eql?, ===, and ==?" about equality in Ruby, with references and stuff. There is a surprising amount of ways to compare for equality in Ruby, each with its own purpose.
Since mathematical meaning is not enough for you, you can compare differently. Like, say, eql? that is heavily used in Hashes to determine if two keys are the same. And it turns out that 1.0 and 1 are different keys! his is what I get in IRB of Ruby 2.1.2:
> 1.0.eql?(1.0)
=> true
> 1.eql?(1)
=> true
> 1.eql?(1.0)
=> false
Ruby is coercing the operands of == (as needed) to the same type, then performing a comparison of their numeric values. This is normally what you want for numeric comparisons.
Most languages will automatically increase the precision of a variable's type in order to perform operations on them such as compare, add, multiply, etc. They "usually" do not decrease the precision, nor unnecessarily increase it. E.g. 1/2 = 0, but 1.0/2.0 = 0.5.

string.to_f precision issue when the value trailed by two zeros

I get input string "400.00" and need to display 400.00.
Expected float_value :400.00
I used to_f to do the task since to_i will return 400 alone. The code is
x="400.00"
float_value=x.to_f
But in this case, I am getting the output as 400.0 which is not acceptable for my case.
current float_value :400.0
Both are equal and have no difference, but its not for any calculation purpose, its for some other display purpose.
Use sprintf for fomatting:
sprintf("%.2f", 400)
#=> "400.00"

Convert cents into dollar string in Ruby without use of BigDecimal

I want to convert from cents to dollars correctly in Ruby. I will never have to work with fractions of cents.
Is it possible to do this correctly (without floating point errors) without having to use BigDecimal?
E.g., cents to dollars
"99" => "0.99"
"324" => "3.24"
The following seems to work, but is it correct?
(cents.to_i/100.0).to_s
Update: I noticed the line above doesn't work if cents = "10287349283923497624861294712974892742837833".
As Micheal Kohl already answered: Take a look to the money gem.
Example:
require 'money'
Money.use_i18n = false #https://stackoverflow.com/q/31133229/676874
puts Money.new( 99, 'USD')
puts Money.new(324, 'USD')
The following seems to work, but is it correct?
(cents.to_i/100.0).to_s
On the first look, it is ok, but:
cents = '10'
p (cents.to_i/100.0).to_s # -> '0.1'
You don't have 2 digits.
Alternative:
p '%.2f' % (cents.to_i/100.0) # -> '0.10'
You can consider using Rationals as well. However, I am not sure do they get converted to floats when sprintf-ed:
"%.2f" % Rational("324".to_i,100)
#=> "3.24"
"%.2f" % Rational("99".to_i,100)
#=> "0.99"
"%.2f" % Rational("80".to_i,100)
#=> "0.80"
"%.2f" % Rational("12380".to_i,100)
#=> "123.80"
If they're stings already you could use string manipulation and bypass the numeric problems completely:
# There are, of course, all sorts of ways to do this.
def add_decimal(s)
pfx = [ '0.00', '0.0', '0.' ]
if(pfx[s.length])
s = pfx[s.length] + s
else
s = s.dup
s[-2, 0] = '.'
end
s
end
add_decimal('') # "0.00"
add_decimal('1') # "0.01"
add_decimal('12') # "0.12"
add_decimal('123') # "1.23"
add_decimal('1234') # "12.34"
add_decimal('12345') # "123.45"
No precision issues, no floating point, no bignums, no Rational, nothing tricky, nothing clever. Some simple modifications would be needed to deal with negative values but that will be as simple as what's already there.
Personally I wouldn't try to re-invent this specific wheel and go with the money gem. From the docs (emphasis added):
Features
Provides a Money class which encapsulates all information about an
certain amount of money, such as its value and its currency.
Provides a Money::Currency class which encapsulates all information about a
monetary unit.
Represents monetary values as integers, in cents. This
avoids floating point rounding errors.
Represents currency as
Money::Currency instances providing an high level of flexibility.
Provides APIs for exchanging money from one currency to another.
Has
the ability to parse a money and currency strings into the
corresponding Money/Currency object.
Here's a one-line method that also simply uses string manipulation thereby completely bypassing the numeric issues:
cents.rjust(3, "0").insert(-3, ".")
These answers are fairly old, so I wanted the next person to know there's an easier way (if you're using Rails).
ActiveSupport::NumberHelper.number_to_currency(111048.fdiv(100))
There's currency and precision options. See Rails documentation
You can use fdiv for this purpose. It returns the floating point result after division of two numbers
-> price.to_i.fdiv(100)
For example: '123'.to_i.fdiv(100) -> 1.23

Calculating the size of an Array pack struct format in Ruby

In the case of e.g. ddddd, d is the native format for the system, so I can't know exactly how big it will be.
In python I can do:
import struct
print struct.calcsize('ddddd')
Which will return 40.
How do I get this in Ruby?
I haven't found a built-in way to do this, but I've had success with this small function when I know I'm dealing with only numeric formats:
def calculate_size(format)
# Only for numeric formats, String formats will raise a TypeError
elements = 0
format.each_char do |c|
if c =~ /\d/
elements += c.to_i - 1
else
elements += 1
end
end
([ 0 ] * elements).pack(format).length
end
This constructs an array of the proper number of zeros, calls pack() with your format, and returns the length (in bytes). Zeros work in this case because they're convertible to each of the numeric formats (integer, double, float, etc).
I don't know of a shortcut but you can just pack one and ask how long it is:
length_of_five_packed_doubles = 5 * [1.0].pack('d').length
By the way, a ruby array combined with the pack method appears to be functionally equivalent to python's struct module. Ruby pretty much copied perl's pack and put them as methods on the Array class.

Resources