Convert 9999999999999999999999.001 to "9999999999999999999999.001" in ruby - 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?

Related

Ruby not allowing me to compare values

I made the code below, but when I run it, including if I pick the correct number, the console prints the debug. Why is this? What am I doing wrong?
puts 'Welcome to the number guessing game. I will pick a number between 1-100. It will be your job to guess the number. If you are incorrect, I will tell you if your guess is higher or lower than my number, I will let you know.'
puts "Time to guess!"
mine = (rand(1..100))
puts mine
grabber = gets.chomp!
if mine == grabber
puts 'That\'s it!'
else
print 'debug'
end
You're comparing values of two different types. gets returns a string, rand(1..100) returns an integer. You can't compare them directly. You need to convert them to the same type, either both integer or both string.
Try using to_i on the string to convert it to an integer:
if mine == grabber.to_i
As #meagar said, you are comparing different types (a string from gets and a number from rand) which will always returning false.
That being said, you have a couple of different ways to coerce/convert datatypes in ruby.
The most common one, as #maeger showed, is using to_i, however it can lead to some strange behaviours as any string that isn't easily parsed as an integer will return 0.
2.5.3 :001 > 'potato'.to_i
=> 0
2.5.3 :002 > '0xff'.to_i
=> 0
If you want to avoid this you can use Integer(arg), this is actually a method defined in Kernel that will do its best to verify if the string is actually convertible into an integer and if it fails it will raise an ArgumentError.
2.5.3 :001 > Integer('potato')
=> ArgumentError (invalid value for Integer(): 'potato')
2.5.3 :002 > Integer('2')
=> 2
2.5.3 :003 > Integer('0xff') # Hexadecimal
=> 255
2.5.3 :004 > Integer('0666') # Octal
=> 438
2.5.3 :005 > Integer('0b1110') # Binary
=> 14

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

How to get the current time as 13-digit integer in Ruby?

I have this jQuery function that returns the current time as the number of milliseconds since the epoch (Jan 1, 1970):
time = new Date().getTime();
Is there a way to do the same in Ruby?
Right now, I am using Ruby's Time.now.to_i which works great but returns a 10-digit integer (number of seconds)
How can I get it to display the number of milliseconds, as in jQuery?
require 'date'
p DateTime.now.strftime('%s') # "1384526946" (seconds)
p DateTime.now.strftime('%Q') # "1384526946523" (milliseconds)
Javascript's gettime() returns the number of milliseconds since epoch.
Ruby's Time.now.to_i will give you the number of seconds since epoch. If you change that to Time.now.to_f, you still get seconds but with a fractional component. Just multiply that by 1,000 and you have milliseconds. Then use #to_i to convert it to an integer. And you end up with:
(Time.now.to_f * 1000).to_i
(Time.now.to_f * 1000).to_i should do the same thing.
Using strftime, you can get the number of seconds and append fractional milliseconds (or smaller units, if needed):
2.2.2 :001 > t = Time.new
=> 2015-06-02 12:16:56 -0700
2.2.2 :002 > t.strftime('%s%3N')
=> "1433272616888"
Note though that this doesn't round, it truncates, as you can see with to_f or if you go out to microseconds:
2.2.2 :003 > t.to_f
=> 1433272616.888615
2.2.2 :004 > t.usec
=> 888615
and the to_f / to_i solution has the same problem (to_i doesn't round, it truncates):
2.2.2 :009 > (t.to_f * 1000).to_i
=> 1433272616888
so if you really care about millisecond accuracy, a better bet may be to_f with round:
2.2.2 :010 > (t.to_f * 1000).round
=> 1433272616889
That said, as noted in the docs, "IEEE 754 double is not accurate enough to represent the number of nanoseconds since the Epoch", so if you really really care, consider to_r instead of to_f --
2.2.2 :011 > (t.to_r * 1000).round
=> 1433272616889
-- although if you're only rounding to milliseconds you're probably fine.
Be careful, don't get confused. The fact that Ruby supports the idea of fractional seconds as a float doesn't actually make it a floating point number. I got into trouble with this when I was doing Wireshark timestamp time comparisons in Python... the time calculations in the pcap-ng just weren't working. It was only when I treated the two parts (integral seconds and integral nanoseconds) as both integers was I able to get proper numbers.
That's because floating point numbers have Accuracy problems. Indeed, a quick bit of Ruby will show you that to_f does not equal, say, nsec:
irb(main):019:0> t=Time.now
=> 2015-04-10 16:41:35 -0500
irb(main):020:0> puts "#{t.to_f}; #{t.nsec}"
1428702095.1435847; 143584844
Caveat Programmer. You may be safe to 3 significant digits, but the fact remains: Floating point numbers on computers are approximations. The nanosecond counters on modern computers are integers.
Get a Time object with Time.now, calling #to_i returns a Unix timestamp (seconds from epoch). #to_f gives fractional seconds which you can use to get milliseconds from epoch:
Time.now.to_f * 1000
Use Process.clock_gettime:
>> Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
=> 1644588106765
See https://ruby-doc.org/core-3.1.0/Process.html#method-c-clock_gettime
The typecast Integer(1e6*Time.now.to_f) returns a Bignum that can hold the milliseconds

Float precision in ruby

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

How do I round a float to a specified number of significant digits in Ruby?

It would be nice to have an equivalent of R's signif function in Ruby.
For example:
>> (11.11).signif(1)
10
>> (22.22).signif(2)
22
>> (3.333).signif(2)
3.3
>> (4.4).signif(3)
4.4 # It's usually 4.40 but that's OK. R does not print the trailing 0's
# because it returns the float data type. For Ruby we want the same.
>> (5.55).signif(2)
5.6
There is probably better way, but this seems to work fine:
class Float
def signif(signs)
Float("%.#{signs}g" % self)
end
end
(1.123).signif(2) # => 1.1
(11.23).signif(2) # => 11.0
(11.23).signif(1) # => 10.0
Here's an implementation that doesn't use strings or other libraries.
class Float
def signif(digits)
return 0 if self.zero?
self.round(-(Math.log10(self).ceil - digits))
end
end
I don't see anything like that in Float. Float is mostly a wrapper for the native double type and given the usual binary/decimal issues, I'm not that surprised that Float doesn't allow you to manipulate the significant digits.
However, BigDecimal in the standard library does understand significant digits but again, I don't see anything that allows you to directly alter the significant digits in a BigDecimal: you can ask for it but you can't change it. But, you can kludge around that by using a no-op version of the mult or add methods:
require 'bigdecimal'
a = BigDecimal.new('11.2384')
a.mult(1, 2) # the result is 0.11E2 (i.e. 11)
a.add(0, 4) # the result is 0.1124E2 (i.e. 11.24)
The second argument to these methods:
If specified and less than the number of significant digits of the result, the result is rounded to that number of digits, according to BigDecimal.mode.
Using BigDecimal will be slower but it might be your only choice if you need fine grained control or if you need to avoid the usual floating point problems.
Some of the previous answers and comments have alluded to this solution but this is what worked for me:
# takes in a float value and returns another float value rounded to
# given significant figures.
def round_to_sig_figs(val, sig_figs)
BigDecimal.new(val, sig_figs).to_f
end
You are probably looking for Ruby's Decimal.
You could then write:
require 'decimal/shortcut'
num = 1.23541764
D.context.precision = 2
num_with_2_significant_digits = +D(num.to_s) # => Decimal('1.2')
num_with_2_significant_digits.to_f # => 1.2000000000000002
Or if you prefer to use the same syntax add this as a function to class Float like this:
class Float
def signif num_digits
require 'decimal/shortcut'
D.context.precision = num_digits
(+D(self.to_s)).to_f
end
end
Usage would then be the same, i.e.
(1.23333).signif 3
# => 1.23
To use it, install the gem
gem install ruby-decimal
#Blou91's answer is nearly there, but it returns a string, instead of a float. This below works for me:
(sprintf "%.2f", 1.23456).to_f
So as a function,
def round(val, sig_figs)
(sprintf "%.#{sig_figs}f", val).to_f
end
Use sprintf if you want to print trailing zeros
2.0.0-p353 :001 > sprintf "%.3f", 500
=> "500.000"
2.0.0-p353 :002 > sprintf "%.4f", 500
=> "500.0000"
2.0.0-p353 :003 >

Resources