How to pass a variable with decimal places in Ruby - ruby

I am having real difficulty with this and every answer I have seen doesnt seem to work. I have been able to pass a value such as 1.44 as 1.00 but the two decimal values are being lost. I have a number of values passed from a from which i then want to submit to an api via a call. The code is below:
IncomeWagesWeekly = params[:WagesWeekly].to_i
How do i ensure that when this is passed the two decimal places are present. Thanks for any help.

You don't have in Ruby language such thing as fixed digits after decimal point.
1 is the same as 1.00 as it was rightfully mentioned before in comment (almost the same).
If you don't check it's type like that:
1.is_a? Integer # => true
1.is_a? Float # => false
it is all the same.
Just use 1.44.to_i.
If by some reason you want to have an instance of Float instead use to_f method. To crop number explicitly you should use round, or ceil, or floor method:
1.44.round.to_f # => 1.0
1.55.round.to_f # => 2.0
1.44.ceil.to_f # => 2.0
1.55.ceil.to_f # => 2.0
1.44.floor.to_f # => 1.0
1.55.floor.to_f # => 1.0

Maybe don't try to make in integer and use to_f instead. Also consider any sprintf( "%0.02f", your_number), but it returns string.

First, it seems that you want to do fixed-point arithmetic here. So, using a floating point number is not the right choice, since floating point calculations can produce mathematically incorrect results.
A solution for this would be either to stick with Integers (as has been suggested already), or to use the data type BigDecimal, which is defined in the Ruby standard library, and in particular its methods fix, frac and to_digits.
Now to your database: You didn't say what database you are using, and how you pass the values to it, but in general, it is a bad idea to store a non-integer value into a database-field which is supposed to accept integers. As you observed, the fractional part was dropped. Correct behaviour.
You could redefine your database schema, or you can convert your decimal value by yourself into something which matches the field definition in the database. Which way to go, depends on what you want to actually do with this value afterwards. For instance, if you just want to display it, but not perform any calculations, you could use a string. Or, if you know that the number of fractional digits don't exceed a certain limit, you could define a suitable numeric format for the database column - etc.

Related

Division of two numbers in freemarker - incorrect result

I'm using freemarker 2.3.26 with Kotlin. I have two variables in my template and I need to divide them.
Variable ${a} is Int and its value is 28.
Variable ${b} is Int and its value is 5.
Output of ${a/b} should be 5, but really it is 6.
I've found out that this is probably caused by rounding
28/5 = 5.6 = rounds to 6
How to correctly handle this? Is there any other - more elegant - way than this?
${(a/b)?floor}
Your number_format configuration setting must be something like '0', hence the decimals are lost when the number is converter to string. I would never use such a number_format, because it's confusing and all. With a usual number_format like '0.####', you get 5.6, because 5 / 6 is indeed 5.6, and if you need to floor it, then you need to be explicit about it. Now, if you are sure you want to drop the decimals during formatting (but again, I think it's a dangerous practice), then use a number_format like '0;;roundingMode=floor' (since FreeMarker 2.3.24), and then ${28/5} will print 5.
Another, probably better solution is that if both numbers are of integer Java types, then you do integer division (like Java). That's possible by setting the arithmetic_engine configuration setting to conservative. That's again not a good practice in general, because the template language doesn't declare the type of the variables, so it's not obvious what a/b will do. As the data-model evolves, and you switch from BigDecimal to an integer Java type, that can lead to wrong output.

A method for creating a huge number

RubyMonk makes a point on how you can use underscores for convenience to write large numbers that can become difficult to read without demarcation.
Their task is: Try using underscores to make a huge, readable number. They provide this code:
def describe(something)
puts "I am a: #{something.class} and I look like: #{something}"
end
def big_num
# create a huge number
end
describe(big_num)
Could anyone explain how I would go about creating a huge number? According to the error messages below, I have to use underscores in the code to make it pass.
RubyMonk expects a object of class Bignum, which is part of the standard Ruby library. From the documentation:
Bignum objects hold integers outside the range of Fixnum. Bignum
objects are created automatically when integer calculations would
otherwise overflow a Fixnum.
So you just have to create a number that is bigger than what Fixnum can handle. For example, this will pass RubyMonk's spec:
def big_num
5_000_000_000_000_000_000_000
end
Because the number is bigger than Fixnum can handle, Ruby automagically returns a Bignum instead. For example, try running this:
5_000_000_000.class
# => Fixnum
5_000_000_000_000_000_000_000.class
# => Bignum
Ruby allows using underscores as placeholders (i.e. they are only to increase readability for humans and are otherwise ignored). So your big_num method can simply have one line:
return 1_000_000_000_000_000_000_000_000_000_000_000
And calling that will return 1000000000000000000000000000000000
(the return keyword is optional)
Ruby allows you to put underscores while writing literal numbers so that large numbers can be easier to read.
The convention goes that you shouldn't put underscores if the number is 4 or less digits long and on every three numbers if it's longer, starting from right:
10_000_000 # => 10000000
Works for floating numbers too:
10_000.0 # => 10000.0
The underscores are ignored by the interpreter if you put them between two digits:
1_2_3_4_5 # => 12345
After looking at the error message, it is clear that RubyMonk expects Bignum. This is another magic, which the interpreter does transparently - if the number is small enough to be mapped to the architecture's int, the number is an instance of Fixnum:
100.class # => Fixnum
If that is not the case, Ruby automagically uses a dedicated class (Bignum):
(10_000_000**100).class # => Bignum
# 10_000_000 to the power of 100,
# which is a very big number and
# thus stored in Bignum

Comparing two virtually identical BigDecimal numbers in Ruby on Rails

I got a little problem that I really would like to understand. I am using assert_equal to compare two BigDecimal numbers that are supposed to be identical. They actually are except a very little tiny fraction, see below:
-#<BigDecimal:7f4b40e8de78,'0.4021666666 6666666666 666666667E2',36(45)>
+#<BigDecimal:7f4b40e85db8,'0.4021666666 6666666666 6666668E2',36(63)>
I use assert_in_delta in order to not fail the test cases. So I got a reasonable workaround. I do wonder though whether it would be possible to have it equal:
assert_equal (241.30.to_d/6), model.division_function
The model's division_function does exactly the same. It divides a BigDecimal of value 241.3 by the length of an array, which is 6.
There seems to be a very tiny difference in the precision.
I would like to know where that might come from?
Is there a way I can control precision more accurately?
EDIT
I am using Mongoid. It is worth to note that Mongoid offers BigDecimal as a field type, but it is stored as a string. However, I don't think this is the problem. I believe it is a Ruby thing.
EDIT
I got a little further with an example which hints that it is a Ruby issue and not directly related to Rails. Please see below:
irb(main):041:0* amount1 = BigDecimal("241.3")
=> #<BigDecimal:7f49bcb03558,'0.2413E3',18(18)>
irb(main):042:0> amount2 = BigDecimal("1800")
=> #<BigDecimal:7f49bcaf3400,'0.18E4',9(18)>
irb(main):043:0> rate = amount1 / amount2
=> #<BigDecimal:7f49bcae8398,'0.1340555555 5555555555 5555556E0',27(45)>
irb(main):044:0> rate * amount2 #should return amount1 = 241.3, but it does not :-(
=> #<BigDecimal:7f49bcad6a30,'0.2413000000 0000000000 00000008E3',36(45)>
irb(main):045:0>
I reported the bug to the Ruby core team. However, this is not a bug as you can see in the rejection response.
BigDecimal, though offers arbitrary precision, cannot represent numbers like 1/3 precisely. Thus during some arithmetic you can encounter imprecisions.
You can use Rational in Ruby for exact numbers. Exercise caution when doing arithmetics if you wish to keep the result exact.

Rails: Time#to_f precision in JSON?

I'm building a simple JSON API using Rails 3.2.1 and Jbuilder on Ruby 1.8.7 (1.9.x might help me here, but my hosting provider only has 1.8.7).
Since the API consumer expects timestamps as floats, I'm currently just doing a simple to_f on the time attributes:
json.updated_at record.updated_at.to_f #=> 1328242368.02242
But to_f incurs a precision loss. This causes some issues when the client requests records that have been modified since a given point in time, as the SQL query finds the same record that the client uses for reference. I.e. when trying to find "newer" records than the example above, the SQL query (e.g. updated_at > Time.at(1328242368.02242)) returns that same record, since the actual value of updated_at is more precise and fractionally larger than the given timestamp.
In fact, record.updated_at.usec #=> 22425 or 0.022425seconds. Notice the extra decimal.
So optimally, the timestamp should be JSON'ified with 1 extra decimal, e.g. 1328242368.022425, but I can't find a way to make that happen.
updated_at.to_i #=> 1328242368 # seconds
updated_at.usec #=> 22425 # microseconds
updated_at.to_f #=> 1328242368.02242 # precision loss
# Hacking around `to_f` doesn't help
decimals = updated_at.usec / 1000000.0 #=> 0.022425 # yay, decimals!
updated_at.to_i + decimals #=> 1328242368.02242 # dammit!
I've looked around for ways to set the default float precision, but I'm stumped. Any ideas?
Edit: I should add that the API consumer isn't running JavaScript, so the float can have higher precision. It would break JS-compatibility (and thus the JSON spec) to add another digit (decimal or otherwise), since JS floats can't handle that, I believe. So perhaps I need an entirely different approach...
After the deliberation in the comments, the best option seems to be monkeypatching ActiveSupport::JSON to make it handle BigDecimals the same as Numerics:
class BigDecimal
def as_json(options = nil) self end #:nodoc:
def encode_json(encoder) to_s end #:nodoc:
end
This overrides the Rails team's decision to prevent serialised BigDecimals from being parsed as floats (and losing precision) in JSON deserialisers with no support for decimal numbers.

Calculating Event Horizons in Ruby

this is my first post here. I started using Ruby just 2 days ago and think it is an amazing language, however I have been getting stuck. My problem is I am wanting to calculate the event horizon of a black hole given an input defined in the code as "m" This will then be put into a calculation and the size then printed out to the screen. I did need it to be in binary and thats where I am having the issue.
Here is my code so far.
#Event Horizon Calculation Program
G = 6.67*10**-11
m = 20
C = 200000
R = G*m/(C**2)
puts "Here is the result in Binary."
R.to_i(2)
puts R
Now I do realise that the number are not accurate, that dosen't matter at the moment. I just need the function to work.
Thankyou,
Ross.
Your post is not even in a format of asking a question, but guessing from what you wrote, it seems that you are asking how to change your code so that it accepts an input to m and outputs the result. My answer is based on this assumption.
In order to take an input, use the 'gets' method. So, you may want to replace your 'm = 20' line with:
m = gets.to_f
'gets' accepts an input as a string, so you need to convert it to a numeric. to_f changes a string into a float. You can use to_i instead if you want an integer.
You have a line 'R.to_i(2)', and it seems like you want to output this, but you have two problems here. First of all, whatever that creates, it is only creating something in that position, and does not change the value of R, so, in effect, it actually does nothing. Second, ruby can accept numerals in source code written in different bases such decimal, binary, hex, etc., but it only has one internal representation, and you cannot output a numeral in binary. For your purpose, you need to convert it to a string that corresponds to a binary expression. For that, use the 'to_s' method. In fact, the 'to_i' method does not take an argument. Delete your line 'R.to_i(s)', and replace the line 'puts R' with:
puts R.to_s(2)

Resources