Feature #18979
openKernel#sprintf formatting BigDecimal without the loss of precision
Description
Recently I stumbled upon a quirk with Kernel#sprintf
's f
modifier behavior with BigDecimal
. It seems to be not documented neither in Kernel
documentation nor in BegDecimal
one. Also, I didn't find previous discussions about it here.
The "problem" is that formatting BigDecimals with sprintf
with f
field type leads to BigDecimal implicit conversion to a float, and to the loss of precision as the result:
require "bigdecimal"
b3 = BigDecimal(1).div(BigDecimal(3), 3) #=> 0.333e0
b50 = BigDecimal(1).div(BigDecimal(3), 50) #=> 0.33333333333333333333333333333333333333333333333333e0
sprintf "%.50f" % b3 #=> "0.33300000000000001820765760385256726294755935668945"
sprintf "%.50f" % b50 #=> "0.33333333333333331482961625624739099293947219848633"
What makes this arguably even more counter-intuitive is the fact that the same BigDecimal
, converted to Rational
before formatting, is being handled accurately:
sprintf "%.50f" % b3.to_r #=> "0.33300000000000000000000000000000000000000000000000"
sprintf "%.50f" % b50.to_r #=> "0.33333333333333333333333333333333333333333333333333"
I understand that Rational
is part of the core and BigDecimal
is not, I see the difference) and know that the former has it's own API for formatting that preserves the precision. Also, from the architecture point of view the idea that some core feature should be aware of a certain part of the "outer layer" (even if it is a standard library) sounds controversial if not nonsense.
But still, having said that, from the Ruby developer perspective (who in most cases finds stdlib playing nicely with the core), the fact that BigDecimal
can be accurately converted to Rational
, Rational
can be accurately formatted with sprintf
, but BigDecimal
cannot be accurately formatted using sprintf
is quite a subtle thing.
For the sake of the principle of least surprise, it would be really nice if Kernel#sprintf
could handle BigDecimal
on par with Rational
. If we cannot have this "fixed" (or shouldn't have, from the architecture standpoint or for any other reason), I think this subtlety probably should be explicitly explained in the documentation (at minimum for BigDecimal
, but maybe for both).
No data to display