Why does Ruby's 14.5.round() does a better job (giving 15) than "%.0f" % 14.5 (giving 14) - ruby

Why does round() do a better job than the printf type of string formatting?
ruby-1.9.2-p0 > "%.0f" % 14.5
=> "14"
ruby-1.9.2-p0 > "%.0f" % 14.5000001
=> "15"
ruby-1.9.2-p0 > 14.5.round
=> 15

Just because you've learned in school that .5 is always rounded up does not mean that's the only correct way to do it. There are quite a number of different rounding modes; what you're looking at is most likely "round to even", which rounds .5 towards the even integer; this has the advantage of not producing an overall upwards bias like always rounding up does.
High-quality math libraries like Ruby Flt generally provide a way to explicitly choose the rounding mode.

"Better job"? No, not if you care about things averaging out. Hence, round towards even.

Related

Ruby round .5 not correct

Here what's bringing me trouble:
irb> (0.5).round # => 1 YES
irb> (0.075).round(2) # => 0.08 YES
irb> (9.075).round(2) # => 9.07 WHY???
What is going on? How come the result isn't 9.08?
Floating point is tricky. The decimal 9.075 can't be exactly represented as a float. This isn't specific to ruby.
The rounding algorithm in most cases (not including nans and the like) works by multiplying the number by the appropriate power of 10, rounding, and then dividing by that same number. That multiplying by 10 results in some loss of precision.
Floating point number can not represent all number precisely. I advise to read Floating Point - Representable numbers, conversion and rounding.
Since Ruby 2.2 you can use prev_float and next_float to see which are the cloased representable floating points to a given number:
9.075.prev_float
#=> 9.074999999999998
9.075.next_float
#=> 9.075000000000001
As you see 9.075 is between to 9.075000000000001 and 9.074999999999998, therefore the mean is at 9.0749999999999995 and therefore 9.075 rounds down to 9.07.
This has to deal with the way ruby deals with Float. If you convert to Rational it rounds to the closest number : (0.075).to_r.round(2) #=> (7/100) and 9.075.to_r.round(2) #=> (907/100)
More details on the floating point logic (logic used to internaly store floats) : https://en.wikipedia.org/wiki/Floating_point

why does Ruby's Rational class treat string arguments differently from numeric arguments?

I'm using ruby's Rational library to convert the width & height of images to aspect ratios.
I've noticed that string arguments are treated differently than numeric arguments.
>> Rational('1.91','1')
=> (191/100)
>> Rational(1.91,1)
=> (8601875288277647/4503599627370496)
>> RUBY_VERSION
=> "2.1.5"
>> RUBY_ENGINE
=> "ruby"
FYI 1.91:1 is an aspect ratio recommended by Facebook for images on their platform.
Values like 191 and 100 are much more convenient to store in my database than 8601875288277647 and 4503599627370496. But I'd like to understand where this different originates before deciding which approach to use.
The Rational test suite doesn't seem to cover this exact case.
Disclaimer: This is only an educated guess, based on some knowledge on how to implement such a feat.
As Kent Dahl already said, Floats are not precise, they have a fixed precision, which means 1.91 is really 1.910000000000000000001 or something like that, which ruby "knows" should be displayed as 1.91.
"1.91" on the other hand is a string, basically an array of characters: '1', '.', '9', '1'.
This said, here is what you need to do, to build the rational out of floats:
Get rid of the . (mathematically by multiplying the numerator and denominator with 10^x, or multiplying with ten as many times, as there are numbers behind the .)
Find the greatest common denominator (gcd)
Divide num and denom with the gcd
Step 1 however, is a little different for Float and String:
The Float, we will have to multiply with 10^x, where x is (because of the precision) not 2 (as one would think with 1.91), but more something like 16 (remember: 1.9100...1).
For the String, we COULD cast it into a float and do the same trick, but hey, there is an easier way: We just count the number of characters behind the dot (which is 2), remove the dot and multiply the denom with 10^2... This is not only the easier, but also the more precise way.
The big numbers might disappear again, when applying step 3, that's why you will not always get those strange results when dealing with rationals from floats.
TLDR: The numbers will be built differently based on the arguments being String, or FLoat. FLoats can produce long-ass numbers, because precision.
The Float 1.91 is stored as a double which has a given amount of precision, limited by binary presentation. The equivalent Rational object retains this precision a such as possible, so it is huge. There is no way of storing 1.91 exactly in a double, but the value you get is close enough for most uses.
As for the String, it represents a different value - the exact value of 1.91 - and as you create a Rational it retains it better. It is more correct than the Float, UT takes longer to use for calculations.
This is similar to the problem with 1.0/3 as it "goes on forever" 0.333333...etc, but Rational can represent it exactly.

Strange result for Float

1.824999999999999.round(2)
# => 1.82
1.82499999999999999.round(2)
# => 1.83
I don't understand why the result in the first case is 1.82 and for the latter 1.83. My desired result is 1.82 for both cases.
As is well known, a floating number in computer has errors. Notice that both numbers that you have are close to 1.825. In your first case, the number is small enough to be differentiated from 1.825.
1.824999999999999
# => 1.824999999999999
In your second case, having enough 9s makes the value close enough to be considered exactly as 1.825:
1.82499999999999999
# => 1.825
Then, when you apply round, you get 1.83.
In addition to the many excellent explanations here, I'd like to give an example using BigDecimal
o = BigDecimal.new('1.82499999999999999', 17)
2.0.0p247 :011 > o.round(2).to_f
=> 1.82
Also notice that #round takes various modes for rounding, as per the docs.
Ref:
http://www.ruby-doc.org/stdlib-2.1.0/libdoc/bigdecimal/rdoc/BigDecimal.html#method-c-mode
http://www.ruby-doc.org/stdlib-2.1.0/libdoc/bigdecimal/rdoc/BigDecimal.html#method-i-round
After a certain number of (binary) digits, Ruby cannot represent differences between Float literals, even if you've entered the value directly in source code or the console
1.82499999999999999 == 1.825
# => true
If you really need a high level of precision, then you could look at the BigDecimal class. Although you could still enter enough 9s to reach limits in that class.
Floats are notoriously imprecise as sawa and Neil have pointed out.
If this is mainly an issue for testing, you can use assert_in_delta if you're using minitest, or be_within with rSpec

ruby: converting from float to integer in ruby produces strange results

ree-1.8.7-2010.02 :003 > (10015.8*100.0).to_i
=> 1001579
ree-1.8.7-2010.02 :004 > 10015.8*100.0
=> 1001580.0
ree-1.8.7-2010.02 :005 > 1001580.0.to_i
=> 1001580
ruby 1.8.7 produces the same.
Does anybody knows how to eradicate this heresy? =)
Actually, all of this make sense.
Because 0.8 cannot be represented exactly by any series of 1 / 2 ** x for various x, it must be represented approximately, and it happens that this is slightly less than 10015.8.
So, when you just print it, it is rounded reasonably.
When you convert it to an integer without adding 0.5, it truncates .79999999... to .7
When you type in 10001580.0, well, that has an exact representation in all formats, including float and double. So you don't see the truncation of a value ever so slightly less than the next integral step.
Floating point is not inaccurate, it just has limitations on what can be represented. Yes, FP is perfectly accurate but cannot necessarily represent every number we can easily type in using base 10. (Update/clarification: well, ironically, it can represent exactly every integer, because every integer has a 2 ** x composition, but "every fraction" is another story. Only certain decimal fractions can be exactly composed using a 1/2**x series.)
In fact, JavaScript implementations use floating point storage and arithmetic for all numeric values. This is because FP hardware produces exact results for integers, so this got the JS guys 52-bit math using existing hardware on (at the time) almost-entirely 32-bit machines.
Due to truncation error in float calculation, 10015.8*100.0 is actually calculated as 1001579.999999... So if you simply apply to_i, it cuts off the decimal part and returns 1001579
http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
>> sprintf("%.16f", 10015.8*100.0)
=> "1001579.9999999999000000"
And Float#to_i truncates this to 1001579.

Inconsistent rounding in Ruby?

Does Ruby have a bug in its rounding? Why does it behave like this:
>> [1.14, 1.15, 1.16].map{|x| "%.1f" % x}
=> ["1.1", "1.1", "1.2"]
>> [1.4, 1.5, 1.6].map{|x| "%.0f" % x}
=> ["1", "2", "2"]
as in, why does 1.15 get rounded to 1.1, but 1.5 gets rounded to 2? At the very least, isn't this inconsistent? the behaviour is the same in ruby 1.9.1 and ruby 1.8.7.
Take a look at my answer to this question
Why does Perl's sprintf not round floating point numbers correctly?
This may be the same thing
You're using floating point numbers. Floating point numbers aren't precise. See http://en.wikipedia.org/wiki/IEEE_754-2008 for an introduction in the standard.
The short version is: NEVER use floats for anything where you need precision in any way!
It's useful to recall and also quite ironic to contemplate, but floating point numbers only represent exactly: (a) a few fractions or (b) all integers.
So, to have an exact representation a fraction must be composed of (negative) powers of two. So, the following fractions are the only ones between .01 and .99 that are exactly represented:
0.25
0.50
0.75
In other words, FP is perfectly accurate when dealing with integers. Go figure.

Resources