Comparing two virtually identical BigDecimal numbers in Ruby on Rails - ruby

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.

Related

Is there a more efficient way of generating a float to a specific decimal value in Ruby?

I basically want a float that is accurate to the sixth place after the decimal.
Something like 0.123456
This is what I tried to do:
irb(main):001:0> rand = rand(1.000000..100.000000)
=> 97.63428182481212
But I obviously don't want it that precise, so I decided to do
irb(main):002:0> rand = rand(1.000000..100.000000).round(6)
=> 12.944086
But now there's an extra step involved, when it seems like I should be able to do it another way. I am going to have to generate several hundred numbers, so I am not sure if I should screw it and just use .round().
What about this?
rand(1000000)/1000000.0
#=> 0.071203

How to pass a variable with decimal places in 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.

What is the size of a boolean in Ruby?

What is the size of a boolean data type in Ruby? There was a long discussion on Ruby Forum regarding this, but there was no final answer that I could get from it.
Also, how can I find what size it is.
For example if I stored it in an array, how much memory would it take
a=[true, true]
vs
a=[1,1]
Serializing tells us that
Marshal.dump([true,true]).length # => 6
Marshal.dump(true).length # => 3
Marshal.dump([1,1]).length # => 8
Marshal.dump(1).length # => 4
I'm pretty sure that this values does not represent real memory usage, but [true,true] seems to be more effective than [1,1].
The Ruby Language Specification does not specify any particular representation for any object. Every Ruby Implementation is free to choose any representation it wants.
Note also that not being able to tell the representation of an object is the defining characteristic of Object-Oriented Data Abstraction. If it were possible to tell the size of a Boolean, Ruby wouldn't be object-oriented!

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.

Error in rounding off values using .round in Ruby

The following piece of code works perfectly in script/console but returns the following error when i compile the same in a ruby script.:
:in `round': wrong number of arguments (1 for 0) (ArgumentError)
tf={"ph"=>{0=>1.33333333333333, 1=>1.5}, "fee"=>{0=>1.66666666666667}, "test"=>{0=>1.16666666666667, 1=>1.25}, "what"=>{0=>2.0, 1=>2.0}, "for"=>{0=>1.5}, "is"=>{0=>1.83333333333333, 1=>1.75}}
tf.each{|k,v| v.each{|k1,v1| tf[k][k1]=(v1.round(5))}}
Any Ideas ? Cheers !
Float#round seems to work differently in Ruby 1.8 and Ruby 1.9: in 1.8 it complains about the given argument, in 1.9 returns back float properly rounded to the given number of decimals.
But, as the article linked in the other answer wisely says:
you should consider the reason you’re
performing the rounding (or
equivalent) operation. If it’s for
presentation reasons only a better way
might be to use a format string
instead, and leave the original data
intact.
From what it looks like, you are not supposed to pass an argument to the round method. You have passed 5 to it.
If you are trying to round it to 5 decimal places, there is no builtin method for that (that I'm aware of). This is a page that explains how to do so: http://solutions.hans-eric.com/rounding-off-floating-point-numbers-in-ruby

Resources