Open aspiers opened 6 years ago
Yep, this is definitely something I've wanted and I've seen multiple requests for it. We should implement it and consider deprecating asStringPrec in the process.
There's an undocumented fformat
method on String which looks like it may have been intended for this.
https://github.com/supercollider/supercollider/commit/0ff292a7ec7cad871a604654f64cf24e98e63592
I started work on this here https://github.com/mkb218/supercollider/tree/topic-printf
@mkb218 Thanks for the effort! But it should really be done as a C++ primitive, so that we don't reinvent the wheel while introducing new bugs. There is information on writing primitives in the SC help system - http://doc.sccode.org/Guides/WritingPrimitives.html. You could also take a look at the existing string primitives for ideas; the new code will probably go there (https://github.com/supercollider/supercollider/blob/develop/lang/LangPrimSource/PyrStringPrim.cpp).
hey @mkb218 I'm tired of not being a marketable C++ dev and this looks like an afternoon's baby step toward fixing that for me. Are you in the middle of cooking something per the above suggestion, or do you mind if I take a shot?
hi @beatboxchad I will likely not have time to accomplish this one until October. Go for it.
Just an update: I was able to understand the primitive system relatively quickly, and began coding a solution. But in a dev meeting we discussed the bigger picture of the various string formatting facilities in sclang and it was suggested that this issue in context with others was a good initial candidate for an RFC, a process introduced right around the same time.
Day job and mental health stuff has interfered with my ability to volunteer the time needed to research the RFC, but I intend to catch a break and finish this by the end of the year.
Might help someone meanwhile - a sclang implementation of a sibling to asStringPrec
that uses the "%f" specifier
(
// The default asStringPrec uses "%g". This method allows us to instead
// use a "%f" style output.
//
// Example:
//
// ~asStringPrecF.(-2.08008714253083e-07) == "-0.000000208008714"
//
~asStringPrecF = { |f, prec = 14|
var as, c, pre;
as = f.abs.asString;
// Pass through values that don't have an negative exponent
if (as.containsi("e-").not) { f.asStringPrec(prec) } {
// Otherwise split on the exponent
c = as.split($e);
c[0].remove($.);
pre = if(f < 0, "-0.", "0.");
[pre,
"0".extend(c[1].asInteger.neg - 1, $0),
c[0]].join[..(prec + pre.size - 1)]
}
};
~test_asStringPrecF = {
var t, rt;
t = UnitTest.new;
UnitTest.reportPasses = false;
t.assertEquals(
~asStringPrecF.( 2.08008714253083e-07),
"0.00000020800871");
t.assertEquals(
~asStringPrecF.(-2.08008714253083e-07),
"-0.00000020800871");
t.assertEquals(
~asStringPrecF.(9.58003511186689e-07),
"0.00000095800351");
100.do {
rt = 0.5e-3.rand2;
t.assertFloatEquals(~asStringPrecF.(rt).asFloat, rt)
};
};
~test_asStringPrecF.value();
)
Also packaged as a method, if that's more useful:
+ Float {
// The default asStringPrec uses "%g". This method allows us to instead
// use a "%f" style output.
//
// Example:
//
// asStringPrecF(-2.08008714253083e-07) == "-0.000000208008714"
//
asStringPrecF { |prec = 14|
var as, c, pre;
as = this.abs.asString;
// Pass through values that don't have an negative exponent
if (as.containsi("e-").not) { ^this.asStringPrec(prec) } {
// Otherwise split on the exponent
c = as.split($e);
c[0].remove($.);
pre = if(this < 0, "-0.", "0.");
^[pre,
"0".extend(c[1].asInteger.neg - 1, $0),
c[0]].join[..(prec + pre.size - 1)]
}
}
}
I'm having a real struggle trying to achieve the equivalent of a typical
printf
with something like%-5.3f
, or even%.3f
, and I'm not the only one.String#-format
only allows basic interpolation, and even with things likepadRight
andasStringPrec
this is way too hard. It's a must-have core feature for any language really, so it'd be great if it was added to sclang.