When one stringifies a scalar, the string is attached to the scalar as an optimisation. It doesn't remove the existing number. This isn't a bug in D::D, but it doesn't mean that D::D couldn't be changed to avoid adding a string to the scalar. It already does in other circumstances.
(pPOK = has string)
>perl -MDevel::Peek -MData::Dumper -e"$_=0.42; Dumper($_); Dump($_);"
SV = NV(0x3335fc) at 0x328fe4
REFCNT = 1
FLAGS = (NOK,pNOK)
NV = 0.42
>perl -MDevel::Peek -MData::Dumper -e"$_={ qux => 42 }; Dumper($_); Du
+mp($_->{qux});"
SV = IV(0x57b260) at 0x57b264
REFCNT = 1
FLAGS = (IOK,pIOK)
IV = 42
>perl -MDevel::Peek -MData::Dumper -e"$_={ qux => 0.42 }; Dumper($_);
+Dump($_->{qux});"
SV = PVNV(0x5490e4) at 0x25b264
REFCNT = 1
FLAGS = (NOK,POK,pNOK,pPOK)
IV = 0
NV = 0.42
PV = 0x4c9f84 "0.42"\0
CUR = 4
LEN = 36
The problem is that JSON doesn't allow you to specify how to handle a variable that's both a float and a string.