I want to round a BigDecimal in ruby. I know I can use the round function but
the round function gives
(3.2).round(2) => 3.2
I want
(3.2).round(2) => 3.20
(3.20).round(2) => 3.20
(3).round(2) => 3.00
(3.578).round(2) => 3.58
I always want to have 2 decimal places, 3.20 not 3.2
any idea how to get this done ?
try this:
'%.2f' % 3.2
=> "3.20"
'%.2f' % 3
=> "3.00"
'%.2f' % 3.578
=> "3.58"
etc.
Unlike, for example, Java's BigDecimal, Ruby's BigDecimal does not have a per-instance precision. The difference between 3.2 and 3.20 in a Ruby BigDecimal is just formatting.
Related
This question already has answers here:
Is floating point math broken?
(31 answers)
Closed last year.
Running into some weird outputs using floor with params for decimal places.
100.1111.floor(1)
=> 100.1
100.1111.floor(2)
=> 100.11
100.1111.floor(3)
=> 100.111
100.1111.floor(4)
=> 100.111 # why is this even the correct answer
100.1111.floor(5)
=> 100.1111
Why would the floor(4) only give 3 decimal places back and why would floor(5) give the correct response?
I attempted to see if this was an error so I tried a different float.
100.111111.floor(1)
=> 100.1
100.111111.floor(2)
=> 100.11
100.111111.floor(3)
=> 100.111
100.111111.floor(4)
=> 100.1111
100.111111.floor(5)
=> 100.11111
100.111111.floor(6)
=> 100.111111
But it seems that this isnt the case because floor works correctly for other float numbers. I tried with 100.111 and other floats and they all seem to work. Only 100.1111 seems to be giving the issue. Is this a problem with the implementation of Float#floor?
Ruby version is ruby 3.0.3p157 if that helps.
Float point numbers can not represent every number/precision, I suggest you to try the most famous example 0.1 + 0.2 that fails to represent 0.3 in most languages.
If you really care about precision in ruby use BigDecimal
require 'bigdecimal/util'
puts BigDecimal('100.1111').floor(1).to_digits
puts BigDecimal('100.1111').floor(2).to_digits
puts BigDecimal('100.1111').floor(3).to_digits
puts BigDecimal('100.1111').floor(4).to_digits
puts BigDecimal('100.1111').floor(5).to_digits
puts BigDecimal('100.1111').floor(6).to_digits
https://ruby-doc.org/stdlib-3.0.3/libdoc/bigdecimal/rdoc/BigDecimal.html
How to convert 9999999999999999999999.001 to "9999999999999999999999.001" in ruby
I have tried
>> 9999999999999999999999.001.to_s
=> "1.0e+22"
>> "%f" % 9999999999999999999999.001
=> "10000000000000000000000.000000"
Truth is you can't. The number you write is 22 digits and floats in ruby have only 15 digits precision. So when you use this variable already part of its value is kind of "lost" as it is of the class Float.
Use BigDecimal from the standard library.
1.8.7 :005 > require 'bigdecimal'
=> true
1.8.7 :006 > BigDecimal('9999999999999999999999.001')
=> #<BigDecimal:7fe0cbcead70,'0.9999999999 9999999999 99001E22',36(36)>
1.8.7 :007 > BigDecimal('9999999999999999999999.001').to_s
=> "0.9999999999999999999999001E22"
Of course, this example only shows that BigDecimal can handle numbers that big. Wherever you're initially getting your 9999999999999999999999.001 number from needs to get it into BigDecimal as soon as it's calculated / inputted.
You can not do that. The reason is simple: from the very beginning the value of the number is not going to be exactly 9999999999999999999999.001. Floats will have just 15 digits of precision.
However, you can use other type to achieve what you want:
require 'bigdecimal'
a = BigDecimal("9999999999999999999999.001")
a.to_s("F")
>> "9999999999999999999999.001"
For BigDecimal the precision is extended with the requests of bigger real numbers - no restrictions are applied.
Float is faster in calculations, because its meant to use the FPU of the processor directly, but because of that comes the restriction in the precision.
EDIT Especially for #izomorphius and his argument, just a very short code sample:
a = "34.101"
b = BigDecimal(a.to_s)
c = b ** 15
c.to_s("F")
>>> 98063348952510709441484.183684987951811295085234607613193907150561501
Now tell me how otherwise you get the last string?
I learned that it's recommended to use BigDecimal instead of Float, but this one is either a bug or highlights the esoteric nature of Float. It seems that Float#round(2) has a problem with "1.015", "1.025" and "1.035".
1.015.round(2)
=> 1.01 # => WRONG .. should be 1.02
1.025.round(2)
=> 1.02 # => WRONG .. should be 1.03
1.035.round(2)
=> 1.03 # => WRONG .. should be 1.04
1.045.round(2)
=> 1.05 # => CORRECT
1.016.round(2)
=> 1.02 # => CORRECT
round(3) works fine.
1.0015.round(3)
=> 1.002 # => CORRECT
1.235.round(2)
=> 1.24 # => CORRECT
To monkey patch this in a Rails app, I did this:
config/initializers/float_mp.rb
require 'bigdecimal'
class Float
def round(val=0)
BigDecimal.new(self.to_s).round(val).to_f
end
end
This seems to be a weird and expensive work-around. Could this be a bug in Float#round?
AFAICS the ruby round() works correctly. Presumably it's just a wrapper around the round() function in libm anyway.
So the reason is that your floating point literals cannot be represented exactly in binary. E.g. "1.015" printed with a few more decimals gives "1.0149999999999999"; thus when rounding to two decimal digits, 1.01 is closer to the true value than 1.02. And so on for your other examples as well.
Also keep in mind that the default IEEE 754 rounding mode is "Round to nearest, ties to even" which is not the same as "Round to nearest, ties away from zero" which is what you may be familiar with from school.
I'm writing a ruby program that uses floats. I'm having trouble with the precision. For example
1.9.3p194 :013 > 113.0 * 0.01
# => 1.1300000000000001
and therefore
1.9.3p194 :018 > 113 * 0.01 == 1.13
# => false
This is exactly the sort of calculation my app needs to get right.
Is this expected? How should I go about handling this?
This is an inherent limitation in floating point numbers (even 0.01 doesn't have an exact binary floating point representation). You can use the technique provided by Aleksey or, if you want perfect precision, use the BigDecimal class bundled in ruby. It's more verbose, and slower, but it will give the right results:
require 'bigdecimal'
=> true
1.9.3p194 :003 > BigDecimal.new("113") * BigDecimal("0.01")
=> #<BigDecimal:26cefd8,'0.113E1',18(36)>
1.9.3p194 :004 > BigDecimal.new("113") * BigDecimal("0.01") == BigDecimal("1.13")
=> true
In calculation with float you should use sigma method - it means not to compare two values, but compare absolute difference of them with a very little value - 1e-10, for example.
((113 * 0.01) - 1.13).abs<1e-10
I met this problem when I tried to compute 3**557 in irb.
Ruby and MacRuby both are installed in my Mac (OS X 10.8). And the version of ruby is 1.8.7, of MacRuby 0.12 (ruby 1.9.2).
rib and macirb gave me two different answers on computation of 3**557. (macirb's is right.)
$ irb
>> 3**557
=> 54755702179342762063551440788945541007926808765326951193810107165429610423703291760740244724326099993131913104272587572918520442872536889724676586931200965615875242243330408150984753872526006744122187638040962508934109837755428764447134683114539218909666971979603
$ macirb
irb(main):001:0> 3**557
=> 57087217942658063217290581978966727348872586279944803346410228520919738045995056049600505293676159316424182057188730248707922985741467061108015301244570536546607487919981026877250949414156613856336341922395385463291076789878575326012378057561766997352898452974964563
And then I tried something bigger, e.g. 3**5337, and I got the same answer this time.
So, is this a bug in Ruby 1.8.7, or I should use another way to compute exponentiation?
When calculating, Ruby is supposed to convert from Fixnum to Bignum when the numbers go beyond the bounds of Fixnum. For older versions of Ruby, this fails with the ** operator:
$ ruby --version
ruby 1.8.7 (2012-02-08 patchlevel 358) [universal-darwin12.0]
$ irb
>> 2 ** 62
=> 4611686018427387904
>> 2 ** 63
=> -9223372036854775808
>> 2 ** 64
=> 0
Where it fails depends on the word size of the architecture. 64-bit words on the iMac in this example. Internally, the Fixnum is cast to a long integer, and the operator is handled with longs. The longs overflow at word size, and Ruby is ungracefully handling this by returning 0.
Note that the * operator works correctly (converting to Bignum), where the ** fails:
>> a = 2 ** 62
=> 4611686018427387904
>> 2 ** 63
=> -9223372036854775808
>> a * 2
=> 9223372036854775808
>> 2 ** 64
=> 0
>> a * 4
=> 18446744073709551616
Moving to a newer version of Ruby will fix this. If you can't move to a newer version, then avoid using Fixnum and ** with large powers.
Using 1.9.3 produces the correct result. Unless you have a really good reason, try to use 1.9.3 or better since 1.8.7 is being phased out.
It's also worth noting that after testing with 1.8.7-p358 on Linux I get the correct answer as well. it could be a bug in the particular version of 1.8.7 you're using.
This is definitely a bug. It is probably dependent on the processor and/or compilation options.
I wouldn't be surprised if it was fixed by this commit.
As others have stated, only security fixes make it to 1.8.7 nowadays, so upgrade to 1.9.3.
It's not related to exponentiation explicitly. I think it's in some way related to the transition from 63 to 64 bits required for representation, though this doesn't seem 100% consistent.
>> 19**14
=> 799006685782884121
>> 19**15
=> -3265617043834753317
>> (19**14)*19
=> -3265617043834753317
and
>> 2**64-1
=> -1
>> 2**64
=> 0
>> 0x7fffffffffffffff
=> 9223372036854775807
yet
>> 0x8000000000000000
=> 9223372036854775808
Also: running irb in 32-bit mode (arch -i386 irb), I don't see this at this point, but earlier:
>> 19**15
=> 15181127029874798299
>> 2**31
=> -2147483648
Writing your own exponentiation method seems to be another way to do it that doesn't produce errors:
def xpnt(base, exponent)
sum = base
while exponent >= 2
sum = sum * base
exponent -= 1
end
puts sum
end
'10' to any power should begin with a single '1' and be followed by nothing but zeros.
Ruby's ** function:
10 ** 40
=> 10000000000000000000092233720368547758080
Custom xpnt method:
xpnt 10, 40
10000000000000000000000000000000000000000
=> nil