2013/4/5 David MacMahon <davidm / astro.berkeley.edu>:

> Of course you're right about String#to_r being correct.  I think Float#to_s is correct as well.  I think the problem is actually in Rational#to_f.

It is expected that Rational#to_f can error because Float has only
53bit mantissa but Rational can hold more digits.
(Ruby uses double type in C and it is usally IEEE 754 double which has
53bit mantissa.)

> Each distinct Float value has (or should have, IMHO) an unambiguous String representation such that f.to_s.to_f == f, discounting NaN and Infinity for which this relationship doesn't hold due to a limitation (bug?) of String#to_f.
>
> String#to_r works correctly as you pointed out.
>
> The problem occurs because the Rational returned by String#to_r is reduced.  When converting the reduced fraction of this example to Float, Rational#to_f effectively computes:
>
>>> 11512646564871409.to_f/200000000000.to_f
> => 57563.23282435704 <=== does NOT equal original value

Float cannot represent 11512646564871409 exactly.

% ruby -e 'i = 11512646564871409; p i.to_s(2), i.to_s(2).length'
"101000111001101011000011101000111011000111110011110001"
54

The result is just an (good) approximation.

> instead of the un-reduced computation of:
>
>>> 57563232824357045.to_f/1000000000000.to_f
> => 57563.232824357045 <=== DOES equal original value

Float cannot represent 57563232824357045 exactly, too.

% ruby -e 'i = 57563232824357045; p i.to_s(2), i.to_s(2).length'
"11001100100000010111010010001100100111100111000010110101"
56

The equality is just a luck.
There are 7 integers to print 57563.232824357045.

% ruby -e '-7.upto(7) {|off|
p((57563232824357045+off).to_f/1000000000000.to_f) }'
57563.23282435704
57563.23282435704
57563.23282435704
57563.23282435704
57563.23282435704
57563.23282435704
57563.23282435704
57563.232824357045
57563.232824357045
57563.232824357045
57563.232824357045
57563.232824357045
57563.232824357045
57563.232824357045
57563.23282435706

It seems your requirement is too strong for Float.
-- 
Tanaka Akira