public
Created

Why is Float#to_s not returning what I'd expect?

  • Download Gist
ruby193-float_to_s_wtf.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14
>> 12345678901234566.0.to_s
=> "12345678901234566.0"
 
>> 12345678901234567.0.to_s
=> "12345678901234568.0"
 
>> 12345678901234568.0.to_s
=> "12345678901234568.0"
 
>> 12345678901234569.0.to_s
=> "12345678901234568.0"
 
>> 123456789012345678.0.to_s
=> "123456789012345680.0"

That's ... no good.

Which one of those do you think is an error?

12345678901234567.to_s(2).length == 54
Floats can only store 53 bits, so it its float equivalent is inexact.

@marcandre

12345678901234569.0.to_s
=> "12345678901234568.0"

but I guess my assumption is wrong. I understand the precision issue when they are actually floats, but as strings I would expect them to be a digit-for-digit copy as a string, that's all.

They can't be a digit-for-digit copy, as that requires way too much information!

Are you really expecting 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475.to_s to be over 200 characters long?

Ruby converts to string with the following algo: the most accurate of the simplest decimal expansion that uniquely maps to that float representation. I.e. you are guaranteed that it round trips any_float.to_s.to_f == any_float, it is unique, i.e. float_a.to_s == float_b.to_s if and only if float_a == float_b. It's simplest in that you can't remove a decimal of the string without changing the resulting float.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.