Im currently getting this result for multiplication
(0.01317818 * 0.00014300)
=> 1.88447974e-06
How can I make the returned result
(0.01317818 * 0.00014300)
=> 0.00000188
Across the whole system
To get 8 decimal places:
"%.8f" % (0.01317818 * 0.00014300)
=> 0.00000188
A bit simpler than using big decimal.
You can use bigdecimal. It is a gem.
require 'bigdecimal'
a = BigDecimal.new('0.01317818')
b = BigDecimal.new('0.00014300')
c = a * b
Related
I have a list of float numbers, representing currency, and I need to turn them into integers, for precision.
The task is to turn float numbers into integers, likes:
0.95 => 95
1 => 100
1,465.01 => 146501
The problem is:
I don't have access to change the input csv files
The numbers came in a variety of ways (1.00, 1.0, 1, .95, 0.95, etc)
How can, safely, I turn these numbers into integers?
Some examples of my problem:
('16.81'.to_f * 100).to_i => 1680
('16.81'.to_f * 100_00).to_i / 100 => 1681
('342.28'.to_f * 100).to_i => 34228
('342.28'.to_f * 100_00).to_i / 100 => 34227
__ EDIT __
By the way, I'm using ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin19]
Floating point numbers can't necessarily represent all decimal numbers. This is explained in Is floating point math broken?. As such, when dealing with floats, you are always a bit uncertain and usually need to use rounding to get a desired number.
From your examples, ('16.81'.to_f * 100) results in 1680.9999999999998. Getting the integer value from that cuts off the fractional part, resulting in 1680. By using round instead, you can get the desired integer (which also solves the issue of partial cents). When relying on this, please note the details of how Ruby rounds exactly, specifically the optional half argument).
Instead of relying on Floats however, a better idea is to use BigDecimal numbers instead which allow arbitrary precision floating point numbers.
require 'bigdecimal'
(BigDecimal('16.81') * 100).round
# => 1681
(BigDecimal('.95') * 100).round
# => 95
number = '1,465.01'
# remove any "additional" characters
normalized = number.gsub(/[^-+0-9.]/, '')
(BigDecimal(normalized) * 100).round
# => 146501
In the last example, I have shown how you might cleanup your "human-readable" numbers for consistent parsing. Depending on your source data, you might need to perform additional changes (e.g. if you might have values such as '1.465,01' as is common in e.g. some European countries).
Use Bigdecimal for float numbers and append .to_i to convert it in integer
require 'bigdecimal'
(BigDecimal('16.81') * 100).to_i # 1681
(BigDecimal('342.28') * 100).to_i # 34228
For more details you can refer https://corainchicago.github.io/blog/why-does-ruby-add-numbers-wrong/
How do I turn "1.5k" into 1500 or "1,766" into "1766" with ruby or rails?
Thanks!
You can do it using ruby without rails.
n = "1,200.5k"
n = n.to_s.gsub(/,+/, '')
n = (n[-1] == 'k' ? n[0...-1].to_f * 1000 : n).to_i
puts n
As for the case "1.5k" you can write a quick method that, if the .to_i() fails, looks for a k as the last character. You can get the last character by doing num_str[-1, 1], where num_str is the original string.
For the other case, I would recommend looking into the money gem. num = Money.parse("1,766").
I'm trying to generate random data in my rails application.
But I am having a problem with decimal amount. I get an error
saying bad value for range.
while $start < $max
$donation = Donation.new(member: Member.all.sample, amount: [BigDecimal('5.00')...BigDecimal('200.00')].sample,
date_give: Random.date_between(:today...Date.civil(2010,9,11)).to_date,
donation_reason: ['tithes','offering','undisclosed','building-fund'].sample )
$donation.save
$start +=1
end
If you want a random decimal between two numbers, sample isn't the way to go. Instead, do something like this:
random_value = (200.0 - 5.0) * rand() + 5
Two other suggestions:
1. if you've implemented this, great, but it doesn't look standard Random.date_between(:today...Date.civil(2010,9,11)).to_date
2. $variable means a global variable in Ruby, so you probably don't want that.
UPDATE --- way to really get random date
require 'date'
def random_date_between(first, second)
number_of_days = (first - second).abs
[first, second].min + rand(number_of_days)
end
random_date_between(Date.today, Date.civil(2010,9,11))
=> #<Date: 2012-05-15 ((2456063j,0s,0n),+0s,2299161j)>
random_date_between(Date.today, Date.civil(2010,9,11))
=> #<Date: 2011-04-13 ((2455665j,0s,0n),+0s,2299161j)>
I have to send the amount of some price in cents to Stripe in order to make charge against a card. In my app, the total_price value is a decimal, i.e in dollars and cents. Obviously, I can convert this to cents by multiplying by 100:
total_price * 100
But the result is still a decimal, and Stripe gives me an 'Invalid amount' error. I know there can be issues with rounding floats. I want to know the safest way to cast my total_price to an integer in Rails. I have seen some reference to a money gem but is this necessary in this case?
Ruby has several methods available to floats, depending on what you need:
(cart.total_price * 100).to_i (discards all decimals)
(cart.total_price * 100).round # .round(numofdecimals)
(cart.total_price * 100).floor # 1.3 => 1
(cart.total_price * 100).ceil # 1.3 => 2
(cart.total_price * 100).to_r #to rationals, e.g. 2.5 => 5/2
I hope this helps.
There is actually a very easy way to do this, directly from a decimal without converting to a float first. Given:
decimal price = BigDecimal('100.00')
You can use BigDecimal's fix() and frac() methods to take the first part, multiply by 100 and add the fraction:
price_in_cents = ((decimal_price.fix * 100) + decimal_price.frac).to_i
Another seemingly simple possibility is to use string manipulation. You can convert the decimal to a string, strip the period, and convert to integer like:
price_in_cents = decimal_price.to_s.sub('.', '').to_i
But the gotcha here is that decimal_price.to_s == '100.0' not '100.00'. If there are non-zero digits in the fraction, then they would be preserved in the conversion, but otherwise one is lost. So if decimal_price == 111.11 then the above string manipulation would return the expected result.
I am storing a cost in my application. The cost is not formatted in the database. For example: 00.00 saves as 0, 1.00 saves as 1, and 40.50 saves as 40.5
I need to read these values from the database and convert them to strings for dollars and cents. For example: 0 --> cost_dollars = "00" & cost_cents = "00", 1 --> cost_dollars = "01" & cost_cents = "00", 40.5 --> cost_dollars = "40" & cost_cents = "50".
Is there an easy way to do this in ruby on rails? Or does someone have code that does this?
Thanks!
You can accomplish that with this little bit of Ruby code:
fmt = "%05.2f" % cost
cost_dollars, cost_cents = fmt.split '.'
If you're trying to format dollar values in a view, you should look at number_to_currency in ActionView::Helpers::NumberHelper.
>> bd = BigDecimal.new "5.75"
>> include ActionView::Helpers
>> number_to_currency(bd)
=> "$5.75"
As for breaking up the value into separate dollars and cents, my first question would be, "Why?" If you have a good reason, and you're dealing with decimals in your database, then you could do the following.
>> bd = BigDecimal.new "5.75"
>> "dollars:#{bd.truncate} cents:#{bd.modulo(1) * BigDecimal.new('100')}"
=> "dollars:5.0 cents:75.0"
number_to_currency is nice, but it can get expensive; you might want to roll your own if you need to call it a lot.
You should be aware that using a float to store currency can be problematic (and see) if you do a lot of calculations based on these values. One solution is to use integers for currency and count cents. This appears to be the approach used by the money plugin. Another solution is to use a decimal type in your migration, which should work out-of-the-box for modern versions of Rails (> 1.2):
add_column :items, :price, :decimal, :precision => 10, :scale => 2
(:scale is the number of places past the decimal, :precision is the total number of digits.) This will get you BigDecimal objects in Rails, which are a little harder to work with, but not too bad.
Both the integer and decimal approaches are a little slower than floating point. I'm using floats for currency in some places, because I know I won't need to do calculations on the values within Rails, only store and display them. But if you need accurate currency calculations, don't use floats.
Instead of storing as a decimal, store as an integral number of cents. So 1 dollar is stored as 100 in the database.
Alternatively, if you don't mind a bit of performance overhead, check for '.' in the database's value. If it exists, split on '.', and parse the pieces as integers.
sprintf is your friend here:
cost_dollars = sprintf('%02.f', cost)
cost_cents = sprintf('%.2f', cost)