Generating Random Fixed Decimal in Rails - ruby-on-rails

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)>

Related

How to solve Mathematical Expressions in Rails 4 like 6000*70%?

I am using Dentaku gem to solve little complex expressions like basic salary is 70% of Gross salary. As the formulas are user editable so I worked on dentaku.
When I write calculator = Dentaku::Calculator.new to initialize and then enter the command calculator.evaluate("60000*70%") then error comes like below:
Dentaku::ParseError: Dentaku::AST::Modulo requires numeric operands
from /Users/sulman/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/dentaku-2.0.8/lib/dentaku/ast/arithmetic.rb:11:in `initialize'
I have array is which formula is stored like: ["EarningItem-5","*","6","7","%"] where EarningItem-5 is an object and has value 60000
How can I resolve such expressions?
For this particular case you can use basic_salary = gross_salary * 0.7
Next you need to create the number field in your views which accepts 0..100 range. At last, set up the after_save callback and use this code:
model
after_create :percent_to_float
protected
def percent_to_float
self.percent = percent / 100.0
self.save
end
edit:
Of course, you can simply use this formula without any callbacks:
basic_salary = gross_salary / 100.0 * 70
where 70 is user defined value.
Dentaku does not appear to support "percent". Try this instead
calculator.evaluate('60000 * 0.7')

Show full decimal in Ruby

I'd like to print the full decimal value of a number in Ruby. I have this:
number = 0.00000254
number.round(8)
puts "Your number equals: " + number.to_s
The number will always be a maximum of eight places after the decimal and I want to always show them all. However, the above code only returns this:
=> Your number = 0.0
(The rounding is only my attempt to get the decimal places that far, I have no desire to round the number). How can I force Ruby to show up to eight places even when there are zeroes at the end like if the number was .00000100?
I just started learning the language last week so if you could use the example above in your answer, that would be great.
Thanks,
Matt
number = 0.00000254
puts "%.8f" % number
You can use number_with_precision from ActionView::Helpers::NumberHelpe.
> include ActionView::Helpers::NumberHelper
=> Object
> number_with_precision(0.00000254, precision: 8)
=> "0.00000254"

How to count the number of decimal places in a Float?

I am using Ruby 1.8.7 and Rails 2.3.5.
If I have a float like 12.525, how can a get the number of digits past the decimal place? In this case I expect to get a '3' back.
Something like that, I guess:
n = 12.525
n.to_s.split('.').last.size
You should be very careful with what you want. Floating point numbers are excellent for scientific purposes and mostly work for daily use, but they fall apart pretty badly when you want to know something like "how many digits past the decimal place" -- if only because they have about 16 digits total, not all of which will contain accurate data for your computation. (Or, some libraries might actually throw away accurate data towards the end of the number when formatting a number for output, on the grounds that "rounded numbers are more friendly". Which, while often true, means it can be a bit dangerous to rely upon formatted output.)
If you can replace the standard floating point numbers with the BigDecimal class to provide arbitrary-precision floating point numbers, then you can inspect the "raw" number:
> require 'bigdecimal'
=> true
> def digits_after_decimal_point(f)
> sign, digits, base, exponent = f.split
> return digits.length - exponent
> end
> l = %w{1.0, 1.1, 1000000000.1, 1.0000000001}
=> ["1.0,", "1.1,", "1000000000.1,", "1.0000000001"]
> list = l.map { |n| BigDecimal(n) }
=> [#<BigDecimal:7f7a56aa8f70,'0.1E1',9(18)>, #<BigDecimal:7f7a56aa8ef8,'0.11E1',18(18)>, #<BigDecimal:7f7a56aa8ea8,'0.1000000000 1E10',27(27)>, #<BigDecimal:7f7a56aa8e58,'0.1000000000 1E1',27(27)>]
> list.map { |i| digits_after_decimal_point(i) }
=> [0, 1, 1, 10]
Of course, if moving to BigDecimal makes your application too slow or is patently too powerful for what you need, this might overly complicate your code for no real benefit. You'll have to decide what is most important for your application.
Here is a very simple approach. Keep track of how many times you have to multiple the number by 10 before it equals its equivalent integer:
def decimals(a)
num = 0
while(a != a.to_i)
num += 1
a *= 10
end
num
end
decimals(1.234) # -> 3
decimals(10/3.0) # -> 16
Like This:
theFloat.to_s.split(".")[1].length
It is not very pretty, but you can insert it as a method for Float:
class Float
def decimalPlaces
self.to_s.split(".")[1].length
end
end
Can you subtract the floor and then just count how many characters left?
(12.525 -( 12.52­5.floor )).to­_s.length-­2
=> 3
edit: nope this doesnt work for a bunch of reasons, negatives and 0.99999 issues
Olexandr's answer doesn't work for integer. Can try the following:
def decimals(num)
if num
arr = num.to_s.split('.')
case arr.size
when 1
0
when 2
arr.last.size
else
nil
end
else
nil
end
end
You can use this approach
def digits_after_decimal_point(n)
splitted = n.to_s.split(".")
if splitted.count > 1
return 0 if splitted[1].to_f == 0
return splitted[1].length
else
return 0
end
end
# Examples
digits_after_decimal_point("1") #=> 0
digits_after_decimal_point("1.0") #=> 0
digits_after_decimal_point("1.01") #=> 2
digits_after_decimal_point("1.00000") #=> 0
digits_after_decimal_point("1.000001") #=> 6
digits_after_decimal_point(nil) #=> 0

after_update callback issues

I'm trying to recalculate percentages in an after_update callback of my model.
def update_percentages
if self.likes_changed? or self.dislikes_changed?
total = self.likes + self.dislikes
self.likes_percent = (self.likes / total) * 100
self.dislikes_percent = (self.dislikes / total) * 100
self.save
end
end
This doesn't work. The percentage always comes out as a 100 or 0, which completely wrecks everything.
Where am I slipping up? I guarantee that self.likes and self.dislikes are being incremented correctly.
The Problem
When you divide an integer by an integer (aka integer division), most programming languages, including Ruby, assume you want your result to be an Integer. This is mostly due to History, because with lower level representations of numbers, an integer is very different than a number with a decimal point, and division with integers is much faster. So your percentage, a number between 0 and 1, has its decimal truncated, and so becomes either 0 or 1. When multiplied by 100, becomes either 0 or 100.
A General Solution
If any of the numbers in the division are not integers, then integer division will not be performed. The alternative is a number with a decimal point. There are several types of numbers like this, but typically they are referred to as floating point numbers, and in Ruby, the most typical floating point number is of the class Float.
1.0.class.ancestors
# => [Float, Precision, Numeric, Comparable, Object, Kernel]
1.class.ancestors
# => [Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel]
In Rails' models, floats are represented with the Ruby Float class, and decimal with the Ruby BigDecimal class. The difference is that BigDecimals are much more accurate (ie can be used for money).
Typically, you can "typecaste" your number to a float, which means that you will not be doing integer division any more. Then, you can convert it back to an integer after your calculations if necessary.
x = 20 # => 20
y = 30 # => 30
y.to_f # => 30.0
x.class # => Fixnum
y.class # => Fixnum
y.to_f.class # => Float
20 / 30 # => 0
20 / 30.0 # => 0.666666666666667
x / y # => 0
x / y.to_f # => 0.666666666666667
(x / y.to_f).round # => 1
A Solution For You
In your case, assuming you are wanting integer results (ie 42 for 42%) I think the easiest way to do this would be to multiply by 100 before you divide. That pushes your decimal point as far out to the right as it will ever go, before the division, which means that your number is as accurate as it will ever get.
before_save :update_percentages
def update_percentages
total = likes + dislikes
self.likes_percent = 100 * likes / total
self.dislikes_percent = 100 * dislikes / total
end
Notes:
I removed implicit self you only need them on assignment to disambiguate from creating a local variable, and when you have a local variable to disambiguate that you wish to invoke the method rather than reference the variable
As suggested by egarcia, I moved it to a callback that happens before the save (I selected before_save because I don't know why you would need to calculate this percentage on an update but not a create, and I feel like it should happen after you validate that the numbers are correct -- ie within range, and integers or decimal or whatever)
Because it is done before saving, we remove the call to save in the code, that is already going to happen
Because we are not explicitly saving in the callback, we do not risk an infinite loop, and thus do not need to check if the numbers have been updated. We just calculate the percentages every time we save.
Because likes/dislikes is an integer value and integer/integer = integer.
so you can do one of two things, convert to Float or change your order of operations.
self.likes_percent = (self.likes.to_f/total.to_f) * 100
Or, to keep everything integers
self.likes_percent = (self.likes * 100)/total
I'm not sure that this is the only problem that you have, but after_update gets called after the object is saved.
Try changing the update_percentages before - on a before_update or a before_validate instead. Also, remove the self.save line - it will be called automatically later on if you use one of those callbacks.

How do I convert a decimal to string value for dollars and cents in ruby?

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)

Resources