Ruby floor gives incorrect decimal points [duplicate] - ruby

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

Related

math expression Randomly rounding up

In the code below 2/5 results to 0.0 instead of 0.4 which should be correct answer
require 'rubygems'
require 'dentaku'
expression = '2/5'
calculator = Dentaku::Calculator.new
result = calculator.evaluate(expression).to_f
puts "#{result}"
I am using dentaku to evaluate the the math expression the documentation for this gem can be found here: https://github.com/rubysolo/dentaku/blob/master/README.md
Your first stop when searching for an answer to this should have been the official documentation for /:
fix / numeric → numeric_result
Performs division: the class of the resulting object depends on the class of numeric and on the magnitude of the result. It may return a Bignum.
For example:
3/2 # => 1
(3/2).class # => Fixnum
3/2.0 # => 1.5
(3/2.0).class # => Float
Ruby will return a Float if either value is a Float:
3.0/2 # => 1.5
(3.0/2).class # => Float
As far as I can tell, Dentaku treats division of two integers as integer division. Try 2/5.0.
Please try expression = '2.0/5.0' instead, maybe your numbers are considered as integers (you want floats) because of the missing ".0" and so you get an integer result of zero

Ruby Kata Integer conversion troubleshoot [duplicate]

This question already has answers here:
ruby basic data type conversion
(4 answers)
Closed 1 year ago.
I'm solving this ruby kata. Essentially what the code does is to output an integer with five digits.
Example:
5 = 00005
12 = 00012
12345 = 12345
00001234 = 012345
Here is my code:
def solution(value) #00001204
string_value = value.to_s
if string_value.length <= 5
amount_of_zeros = "0" * (string_value.length - 5).abs
puts "Value is #{amount_of_zeros}" + "#{string_value}"
else
start_of_characters = 5 - string_value.length #-3
puts "Value is " + string_value[-1..start_of_characters]
end
end
Everything works fine until I place 00001204. For some reason I get the output 00644. I tried using binding.pry to see what was going on and my number gets converted into 644 from the start. Why is it doing that? The docs don't mention anything about it. I don't how to fix this because on the first line of my method it already turns into 644. Any thoughts?
In ruby, numbers that are 0-prefixed are interpreted as octal. When you pass 00001204 to your method, ruby is assuming that you want the number interpreted as octal. 12048 = 64410.
644.to_s 8
=> "1204"
01204.to_s 10
=> "644"
Check out the Ruby documentation on literals.
as Zajn said it is due to it being interpreted as octal.
You can also use .to_i to force it to be an integer.
>> 000000001204
=> 644
>> "000000001204".to_i
=> 1204
Also take a look at string formatting with % if you just want to output it, since it can clean up your code a lot
>> "%05d" % "000001204".to_i
=> "01204"

Convert 9999999999999999999999.001 to "9999999999999999999999.001" in ruby

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?

Ruby Float#round method behaves incorrectly with round(2)

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.

How do I generate a random number in a certain range with Ruby?

I am trying to get a random year between 1900 and 1980 with Ruby.
So far I have:
puts 'the year was: ' + 1900.to_s + rand(1980).to_s
but this is just adding 1900 and a random number from 0 - 1979 together to look like 19001947.
I think I'm missing something silly but can anyone shed any light?
Ruby 1.9.3
1.9.3p0 :001 > rand(1900..1980)
=> 1946
1.9.3p0 :002 > rand(1900..1980)
=> 1929
1.9.3p0 :003 > rand(1900..1980)
=> 1934
Give this a try
(1900 + rand(81)).to_s
To be clear for passersby
First, there was an issue regarding string concatenation. The original code in the question was concatenating two strings (containing numbers) together. Example:
"1900" + "1920" = "19001920"
Second, there was an issue the range of random numbers being generated. In this case, since we only want a range of 80 years, we want to use rand(81), instead of rand(1980). We then take that result and add it to the base number, which will give us a random number between 1900 and 1980.
puts "The year was #{1900 + rand 81}"
Yes: you're getting the "string catenation" overload of +. Try (1900+rand(80)).to_s
Ruby 1.8+
(1900..1980).to_a[rand(80)]
# or little more Rubyistic
(1900..1980).to_a.shuffle.first
Ruby 1.9+
[*1900..1980].sample

Resources