www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - it's time to change how things are printed

reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
A recent bug report reminded me of how horrible D is at printing custom  
types.

Consider a container type that contains 1000 elements, such as a linked  
list.  If you print this type, you would expect to get a printout similar  
to an array, i.e.:

[ 1 2 3 4 5 ... 1000 ]

If you do this:

writeln(mylist);

then what happens is, writeln calls mylist.toString(), and prints that  
string.

But inside mylist.toString, it likely does things like elem[0].toString()  
and concatenates all these together.  This results in at least 1000 + 1  
heap allocations, to go along with 1000 appends,  to create a string that  
will be sent to an output stream and *discarded*.

So the seemingly innocuous line writeln(mylist) is like attaching a boat  
anchor to your code performance.

There is a better way, as demonstrated by BigInt (whose author refuses to  
implement toString()):

void toString(scope void delegate(scope const(char)[] data), string format  
= null)

What does this do?  Well, now, writeln can define a delegate that takes a  
string and sends it to an output stream.  Now, there is no copying of  
data, no heap allocations, and no need to concatenate anything together!   
Not only that, but it can be given an optional format specifier to control  
output when writefln is used.  Let's see how a linked list would implement  
this function (ignoring format for now):

void toString(scope void delegate(scope const(char)[] data) sink, string  
format = null)
{
    sink("[");
    foreach(elem; this)
    {
       sink(" ");
       elem.toString(sink);
    }
    sink(" ]");
}

It looks just about as simple as the equivalent function that would  
currently be necessary, except you have *no* heap allocations, there is a  
possibility for formatting, and D will be that much better performing.   
Note that using a delegate allows much more natural code which requires  
recursion.

Should we create a DIP for this?  I'll volunteer to spearhead the effort  
if people are on board.

-Steve
Nov 18 2010
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Steven Schveighoffer" <schveiguy yahoo.com> wrote in message 
news:op.vmdglth8eav7ka localhost.localdomain...
A recent bug report reminded me of how horrible D is at printing custom 
types.

 Consider a container type that contains 1000 elements, such as a linked 
 list.  If you print this type, you would expect to get a printout similar 
 to an array, i.e.:

 [ 1 2 3 4 5 ... 1000 ]

 If you do this:

 writeln(mylist);

 then what happens is, writeln calls mylist.toString(), and prints that 
 string.

 But inside mylist.toString, it likely does things like elem[0].toString() 
 and concatenates all these together.  This results in at least 1000 + 1 
 heap allocations, to go along with 1000 appends,  to create a string that 
 will be sent to an output stream and *discarded*.

 So the seemingly innocuous line writeln(mylist) is like attaching a boat 
 anchor to your code performance.

 There is a better way, as demonstrated by BigInt (whose author refuses to 
 implement toString()):

 void toString(scope void delegate(scope const(char)[] data), string format 
 = null)

 What does this do?  Well, now, writeln can define a delegate that takes a 
 string and sends it to an output stream.  Now, there is no copying of 
 data, no heap allocations, and no need to concatenate anything together! 
 Not only that, but it can be given an optional format specifier to control 
 output when writefln is used.  Let's see how a linked list would implement 
 this function (ignoring format for now):

 void toString(scope void delegate(scope const(char)[] data) sink, string 
 format = null)
 {
    sink("[");
    foreach(elem; this)
    {
       sink(" ");
       elem.toString(sink);
    }
    sink(" ]");
 }

 It looks just about as simple as the equivalent function that would 
 currently be necessary, except you have *no* heap allocations, there is a 
 possibility for formatting, and D will be that much better performing. 
 Note that using a delegate allows much more natural code which requires 
 recursion.

 Should we create a DIP for this?  I'll volunteer to spearhead the effort 
 if people are on board.

I like it, *provided that* there's a quick-and-easy way to just get a string when that's all you want. At the very least there should be a standard sink function that's a default argument to toString that just simply builds a string. What we definitely *don't* want is for the user to ever have to write their own sink delegate just to get a string (which I've had to do with Tango on occasion).
Nov 18 2010
parent reply Kagamin <spam here.lot> writes:
Steven Schveighoffer Wrote:

 to!string(x);
 

What's with text(x); ?
Nov 18 2010
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 11/18/10 11:19 AM, Kagamin wrote:
 Steven Schveighoffer Wrote:

 to!string(x);

What's with text(x); ?

text, wtext, and dtext accept a variable number of arguments of all types and create one string by concatenating to!(w|d|)string for all inputs. Andrei
Nov 18 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 18 Nov 2010 10:44:00 -0500, Nick Sabalausky <a a.a> wrote:

 I like it, *provided that* there's a quick-and-easy way to just get a  
 string
 when that's all you want. At the very least there should be a standard  
 sink
 function that's a default argument to toString that just simply builds a
 string. What we definitely *don't* want is for the user to ever have to
 write their own sink delegate just to get a string (which I've had to do
 with Tango on occasion).

to!string(x); (which will probably do the delegate/etc when x.toString is defined) -Steve
Nov 18 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 18 Nov 2010 10:14:07 -0500, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:

 There is a better way, as demonstrated by BigInt (whose author refuses  
 to implement toString()):

 void toString(scope void delegate(scope const(char)[] data), string  
 format = null)

I should also note, I'm not fond of calling this 'toString', but that's how BigInt does it. It seems inaccurate, when the delegate could do just about anything with the data, not just building strings. commence bikeshedding :) -Steve
Nov 18 2010
prev sibling next sibling parent Fawzi Mohamed <fawzi gmx.ch> writes:
On 18-nov-10, at 16:14, Steven Schveighoffer wrote:

 A recent bug report reminded me of how horrible D is at printing  
 custom types.

 Consider a container type that contains 1000 elements, such as a  
 linked list.  If you print this type, you would expect to get a  
 printout similar to an array, i.e.:

 [ 1 2 3 4 5 ... 1000 ]

 If you do this:

 writeln(mylist);

 then what happens is, writeln calls mylist.toString(), and prints  
 that string.

 But inside mylist.toString, it likely does things like  
 elem[0].toString() and concatenates all these together.  This  
 results in at least 1000 + 1 heap allocations, to go along with 1000  
 appends,  to create a string that will be sent to an output stream  
 and *discarded*.

 So the seemingly innocuous line writeln(mylist) is like attaching a  
 boat anchor to your code performance.

 There is a better way, as demonstrated by BigInt (whose author  
 refuses to implement toString()):

 void toString(scope void delegate(scope const(char)[] data), string  
 format = null)

 What does this do?  Well, now, writeln can define a delegate that  
 takes a string and sends it to an output stream.  Now, there is no  
 copying of data, no heap allocations, and no need to concatenate  
 anything together!  Not only that, but it can be given an optional  
 format specifier to control output when writefln is used.  Let's see  
 how a linked list would implement this function (ignoring format for  
 now):

 void toString(scope void delegate(scope const(char)[] data) sink,  
 string format = null)
 {
   sink("[");
   foreach(elem; this)
   {
      sink(" ");
      elem.toString(sink);
   }
   sink(" ]");
 }

 It looks just about as simple as the equivalent function that would  
 currently be necessary, except you have *no* heap allocations, there  
 is a possibility for formatting, and D will be that much better  
 performing.  Note that using a delegate allows much more natural  
 code which requires recursion.

 Should we create a DIP for this?  I'll volunteer to spearhead the  
 effort if people are on board.

I agree wholeheartedly with this, I have always pushed in this direction every time the subject came up. In tango for example exception uses this, also because I did not want memory allocations printing the stacktrace. This is the way used in blip to output everything, I always felt bad in allocating things on the heap. - in object I look for a void desc(void delegate(const(char)[] data) sink) method (well D1, so scope is implied ;) optionally with extra format arguments that don't have to be restricted to a simple string. - i have implemented a writeOut templatized function to easily dump out all kinds of objects to sinks or similar objects with it you write writeOut(sink,object,possiblyExtraArgs); // see in blip.io.BasicIO - I have defined a dumper object (just a struct) and a helper function for easy call chaining, so you can do dumper(sink)("bla:")(myObject)("\n"); - blip.container.GrowableArray completes the offer by giving an easy way to collect the results, and has two helper functions: /// collects what is appended by the appender in a single array and returns it /// it buf is provided the appender tries to use it (but allocates if extra space is needed) T[] collectAppender(T)(void delegate(void delegate(T[])) appender,char[] buf=null){} /// collects what is appended by the appender and adds it at once to the given sink void sinkTogether(U,T)(U sink,void delegate(void delegate(T[])) appender,char[] buf=null){} I find that such an approach works well, is not too intrusive, and is efficient. Fawzi If you take a look at blip.
Nov 18 2010
prev sibling next sibling parent Fawzi Mohamed <fawzi gmx.ch> writes:
On 18-nov-10, at 17:01, Fawzi Mohamed wrote:

 [...]
 If you take a look at blip.

ehm that was a leftover form my editing that I did not see because it was outside the visible area in my mail program... just ignore it. well you *can* look at blip, but well you get the point...
Nov 18 2010
prev sibling next sibling parent Fawzi Mohamed <fawzi gmx.ch> writes:
On 18-nov-10, at 16:53, Steven Schveighoffer wrote:

 On Thu, 18 Nov 2010 10:44:00 -0500, Nick Sabalausky <a a.a> wrote:

 I like it, *provided that* there's a quick-and-easy way to just get  
 a string
 when that's all you want. At the very least there should be a  
 standard sink
 function that's a default argument to toString that just simply  
 builds a
 string. What we definitely *don't* want is for the user to ever  
 have to
 write their own sink delegate just to get a string (which I've had  
 to do
 with Tango on occasion).

to!string(x); (which will probably do the delegate/etc when x.toString is defined)

I don't know I considered using the to!(T) conversion, but decided against it in blip, because I preferred having to for exact conversion, and use another set of methods for string conversion (that is special enough, and sometime used just for debugging, and not invertible). by the way another nice effect of using a simple sink delegate is that you can easily redeclare it at low level and get rid of dependencies (well maybe you suffer a bit converting basic types, but it is doable) Whereas using higher level streams is difficult in the runtime (you easily have object depending on them, forcing you to put them in object.d) Fawzi
Nov 18 2010
prev sibling next sibling parent reply "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
On Thu, 18 Nov 2010 10:14:07 -0500, Steven Schveighoffer wrote:

 A recent bug report reminded me of how horrible D is at printing custom
 types.
 
 Consider a container type that contains 1000 elements, such as a linked
 list.  If you print this type, you would expect to get a printout
 similar to an array, i.e.:
 
 [ 1 2 3 4 5 ... 1000 ]
 
 If you do this:
 
 writeln(mylist);
 
 then what happens is, writeln calls mylist.toString(), and prints that
 string.
 
 But inside mylist.toString, it likely does things like
 elem[0].toString() and concatenates all these together.  This results in
 at least 1000 + 1 heap allocations, to go along with 1000 appends,  to
 create a string that will be sent to an output stream and *discarded*.
 
 So the seemingly innocuous line writeln(mylist) is like attaching a boat
 anchor to your code performance.
 
 There is a better way, as demonstrated by BigInt (whose author refuses
 to implement toString()):
 
 void toString(scope void delegate(scope const(char)[] data), string
 format = null)
 
 What does this do?  Well, now, writeln can define a delegate that takes
 a string and sends it to an output stream.  Now, there is no copying of
 data, no heap allocations, and no need to concatenate anything together!
 Not only that, but it can be given an optional format specifier to
 control output when writefln is used.  Let's see how a linked list would
 implement this function (ignoring format for now):
 
 void toString(scope void delegate(scope const(char)[] data) sink, string
 format = null)
 {
     sink("[");
     foreach(elem; this)
     {
        sink(" ");
        elem.toString(sink);
     }
     sink(" ]");
 }
 
 It looks just about as simple as the equivalent function that would
 currently be necessary, except you have *no* heap allocations, there is
 a possibility for formatting, and D will be that much better performing.
 Note that using a delegate allows much more natural code which requires
 recursion.
 
 Should we create a DIP for this?  I'll volunteer to spearhead the effort
 if people are on board.

First of all, I think Andrei has already implemented this in the write*() functions. I use this toString() style also for std.complex.Complex, and I can print complex numbers no problem. That said, I also think toString is a bad name for this. Especially considering it will be used as an imperative, i.e. obj.toString(sink); instead of s = obj.toString(); I don't really have a good suggestion for an alternative name, though. Perhaps 'output'? I would personally prefer a range-based solution: void output(R)(R sink, string fmt = null) if (isOutputRange!R) { ... } Since not too long ago, a delegate taking T is considered an output range of T. This will allow you to using both a sink delegate and a more conventional output range. I do, however, realise that templates aren't everyone's cup of tea, and that there are situations where they can't be used, so perhaps the delegate solution is best after all. -Lars
Nov 18 2010
parent reply Don <nospam nospam.com> writes:
Lars T. Kyllingstad wrote:
 First of all, I think Andrei has already implemented this in the write*() 
 functions.  I use this toString() style also for std.complex.Complex, and 
 I can print complex numbers no problem.

Really? It doesn't work for me. import std.complex; import std.stdio; void main() { cdouble z2 = 10 + 1.5e-6i; Complex!(double) z; z.re = 10; z.im = 1.5e-6; writefln("z= %.16f z2 = %.16f", z, z2); } Prints: z = 10+1.5e-06i z2 = 10.0000000000000000+0.0000015000000000i
Nov 18 2010
parent reply Don <nospam nospam.com> writes:
Steven Schveighoffer wrote:
 On Thu, 18 Nov 2010 15:13:19 -0500, Don <nospam nospam.com> wrote:
 
 Lars T. Kyllingstad wrote:
 First of all, I think Andrei has already implemented this in the 
 write*() functions.  I use this toString() style also for 
 std.complex.Complex, and I can print complex numbers no problem.

Really? It doesn't work for me. import std.complex; import std.stdio; void main() { cdouble z2 = 10 + 1.5e-6i; Complex!(double) z; z.re = 10; z.im = 1.5e-6; writefln("z= %.16f z2 = %.16f", z, z2); } Prints: z = 10+1.5e-06i z2 = 10.0000000000000000+0.0000015000000000i

Haven't tested, but docs state that "Note that complex numbers are floating point numbers, so the only valid format characters are 'e', 'f', 'g', 'a', and 's', where 's' gives the default behaviour. ***Positional parameters are not valid in this context.***" I'd suggest trying one of those other format types without the numeric parts. -Steve

writefln("z = %f z2 = %f", z, z2); z = 10+1.5e-06i z2 = 10.000000+0.000001i writefln("z = %e z2 = %e", z, z2); z = 10+1.5e-06i z2 = 1.000000e+01+1.500000e-06i writefln("z = %a z2 = %a", z, z2); z = 10+1.5e-06i z2 = 0x1.4p+3+0x1.92a737110e454p-20i
Nov 18 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 11/18/10 1:25 PM, Don wrote:
 Steven Schveighoffer wrote:
 On Thu, 18 Nov 2010 15:13:19 -0500, Don <nospam nospam.com> wrote:

 Lars T. Kyllingstad wrote:
 First of all, I think Andrei has already implemented this in the
 write*() functions. I use this toString() style also for
 std.complex.Complex, and I can print complex numbers no problem.

Really? It doesn't work for me. import std.complex; import std.stdio; void main() { cdouble z2 = 10 + 1.5e-6i; Complex!(double) z; z.re = 10; z.im = 1.5e-6; writefln("z= %.16f z2 = %.16f", z, z2); } Prints: z = 10+1.5e-06i z2 = 10.0000000000000000+0.0000015000000000i

Haven't tested, but docs state that "Note that complex numbers are floating point numbers, so the only valid format characters are 'e', 'f', 'g', 'a', and 's', where 's' gives the default behaviour. ***Positional parameters are not valid in this context.***" I'd suggest trying one of those other format types without the numeric parts. -Steve

writefln("z = %f z2 = %f", z, z2); z = 10+1.5e-06i z2 = 10.000000+0.000001i writefln("z = %e z2 = %e", z, z2); z = 10+1.5e-06i z2 = 1.000000e+01+1.500000e-06i writefln("z = %a z2 = %a", z, z2); z = 10+1.5e-06i z2 = 0x1.4p+3+0x1.92a737110e454p-20i

Clearly there's a bug in the formatting logic. Should be easy to fix, but I'm groping for time at the moment. Don, could you please bugzillize? Thanks. Andrei
Nov 18 2010
parent Don <nospam nospam.com> writes:
Andrei Alexandrescu wrote:
 On 11/18/10 1:25 PM, Don wrote:
 Steven Schveighoffer wrote:
 On Thu, 18 Nov 2010 15:13:19 -0500, Don <nospam nospam.com> wrote:

 Lars T. Kyllingstad wrote:
 First of all, I think Andrei has already implemented this in the
 write*() functions. I use this toString() style also for
 std.complex.Complex, and I can print complex numbers no problem.

Really? It doesn't work for me. import std.complex; import std.stdio; void main() { cdouble z2 = 10 + 1.5e-6i; Complex!(double) z; z.re = 10; z.im = 1.5e-6; writefln("z= %.16f z2 = %.16f", z, z2); } Prints: z = 10+1.5e-06i z2 = 10.0000000000000000+0.0000015000000000i

Haven't tested, but docs state that "Note that complex numbers are floating point numbers, so the only valid format characters are 'e', 'f', 'g', 'a', and 's', where 's' gives the default behaviour. ***Positional parameters are not valid in this context.***" I'd suggest trying one of those other format types without the numeric parts. -Steve

writefln("z = %f z2 = %f", z, z2); z = 10+1.5e-06i z2 = 10.000000+0.000001i writefln("z = %e z2 = %e", z, z2); z = 10+1.5e-06i z2 = 1.000000e+01+1.500000e-06i writefln("z = %a z2 = %a", z, z2); z = 10+1.5e-06i z2 = 0x1.4p+3+0x1.92a737110e454p-20i

Clearly there's a bug in the formatting logic. Should be easy to fix, but I'm groping for time at the moment. Don, could you please bugzillize? Thanks. Andrei

Nov 18 2010
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 11/18/10 7:14 AM, Steven Schveighoffer wrote:
 A recent bug report reminded me of how horrible D is at printing custom
 types.

 Consider a container type that contains 1000 elements, such as a linked
 list. If you print this type, you would expect to get a printout similar
 to an array, i.e.:

 [ 1 2 3 4 5 ... 1000 ]

 If you do this:

 writeln(mylist);

 then what happens is, writeln calls mylist.toString(), and prints that
 string.

 But inside mylist.toString, it likely does things like
 elem[0].toString() and concatenates all these together. This results in
 at least 1000 + 1 heap allocations, to go along with 1000 appends, to
 create a string that will be sent to an output stream and *discarded*.

 So the seemingly innocuous line writeln(mylist) is like attaching a boat
 anchor to your code performance.

 There is a better way, as demonstrated by BigInt (whose author refuses
 to implement toString()):

 void toString(scope void delegate(scope const(char)[] data), string
 format = null)

 What does this do? Well, now, writeln can define a delegate that takes a
 string and sends it to an output stream. Now, there is no copying of
 data, no heap allocations, and no need to concatenate anything together!
 Not only that, but it can be given an optional format specifier to
 control output when writefln is used. Let's see how a linked list would
 implement this function (ignoring format for now):

 void toString(scope void delegate(scope const(char)[] data) sink, string
 format = null)
 {
 sink("[");
 foreach(elem; this)
 {
 sink(" ");
 elem.toString(sink);
 }
 sink(" ]");
 }

 It looks just about as simple as the equivalent function that would
 currently be necessary, except you have *no* heap allocations, there is
 a possibility for formatting, and D will be that much better performing.
 Note that using a delegate allows much more natural code which requires
 recursion.

 Should we create a DIP for this? I'll volunteer to spearhead the effort
 if people are on board.

 -Steve

Yes please. Andrei
Nov 18 2010
prev sibling next sibling parent "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
On Thu, 18 Nov 2010 10:53:20 -0500, Steven Schveighoffer wrote:

 On Thu, 18 Nov 2010 10:44:00 -0500, Nick Sabalausky <a a.a> wrote:
 
 I like it, *provided that* there's a quick-and-easy way to just get a
 string
 when that's all you want. At the very least there should be a standard
 sink
 function that's a default argument to toString that just simply builds
 a string. What we definitely *don't* want is for the user to ever have
 to write their own sink delegate just to get a string (which I've had
 to do with Tango on occasion).

to!string(x); (which will probably do the delegate/etc when x.toString is defined)

On the Phobos list, Andrei suggested a unified toString signature, which I tried out for std.complex.Complex: // If sink is provided, output to it and return null, // otherwise return string. string toString(void delegate(const(char)[] ) sink = null, string fmt = null) { ... } But I don't know, one might as well just define a separate toString(). I prefer the to!string() solution you suggest. -Lars
Nov 18 2010
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 18 Nov 2010 10:14:07 -0500, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:


 Should we create a DIP for this?  I'll volunteer to spearhead the effort  
 if people are on board.

I want to create a DIP, but it looks like I can't edit anything without a 'valid' username. However, when I put schveiguy as a username, it says that's not valid. How do I get a valid username? -Steve
Nov 18 2010
parent jcc7 <jccalvarese gmail.com> writes:
== Quote from Steven Schveighoffer (schveiguy yahoo.com)'s article
 I want to create a DIP, but it looks like I can't edit anything without a
 'valid' username.  However, when I put schveiguy as a username, it says
 that's not valid.  How do I get a valid username?
 -Steve

The username has to be mixed case for whatever reason (e.g., "SchveiGuy" or "StevenSchveighoffer"). From http://www.prowiki.org/wiki4d/wiki.cgi?WelcomeVisitor: << You are welcome to edit pages in this wiki, but please enter your name on your "Preferences" page by clicking this link: Edit Preferences There is no formal "account creation" required. Note, however, that the name you enter on the "Preferences" page must be all one word in "MixedCase" or you will get an invalid username error. So "JimbobMcCranston" is ok, but "Jimbob McCranston" and "jimbob" are not. The main reason for requiring a name to be set in the preferences is to reduce the amount of spam that has been put into this wiki by spam bots.


jcc7
Nov 18 2010
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On Thu, 18 Nov 2010 17:01:03 +0000 (UTC)
"Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> wrote:

 That said, I also think toString is a bad name for this.  Especially=20
 considering it will be used as an imperative, i.e.
=20
   obj.toString(sink);

obj.writeTo(sink); Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Nov 18 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 18 Nov 2010 15:13:19 -0500, Don <nospam nospam.com> wrote:

 Lars T. Kyllingstad wrote:
 First of all, I think Andrei has already implemented this in the  
 write*() functions.  I use this toString() style also for  
 std.complex.Complex, and I can print complex numbers no problem.

Really? It doesn't work for me. import std.complex; import std.stdio; void main() { cdouble z2 = 10 + 1.5e-6i; Complex!(double) z; z.re = 10; z.im = 1.5e-6; writefln("z= %.16f z2 = %.16f", z, z2); } Prints: z = 10+1.5e-06i z2 = 10.0000000000000000+0.0000015000000000i

Haven't tested, but docs state that "Note that complex numbers are floating point numbers, so the only valid format characters are 'e', 'f', 'g', 'a', and 's', where 's' gives the default behaviour. ***Positional parameters are not valid in this context.***" I'd suggest trying one of those other format types without the numeric parts. -Steve
Nov 18 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 18 Nov 2010 16:25:28 -0500, Don <nospam nospam.com> wrote:

 Steven Schveighoffer wrote:

  I'd suggest trying one of those other format types without the numeric  
 parts.
  -Steve

writefln("z = %f z2 = %f", z, z2); z = 10+1.5e-06i z2 = 10.000000+0.000001i writefln("z = %e z2 = %e", z, z2); z = 10+1.5e-06i z2 = 1.000000e+01+1.500000e-06i writefln("z = %a z2 = %a", z, z2); z = 10+1.5e-06i z2 = 0x1.4p+3+0x1.92a737110e454p-20i

Then I agree, it doesn't work. -Steve
Nov 18 2010
prev sibling parent "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
On Thu, 18 Nov 2010 21:13:19 +0100, Don wrote:

 Lars T. Kyllingstad wrote:
 First of all, I think Andrei has already implemented this in the
 write*() functions.  I use this toString() style also for
 std.complex.Complex, and I can print complex numbers no problem.

Really? It doesn't work for me. import std.complex; import std.stdio; void main() { cdouble z2 = 10 + 1.5e-6i; Complex!(double) z; z.re = 10; z.im = 1.5e-6; writefln("z= %.16f z2 = %.16f", z, z2); } Prints: z = 10+1.5e-06i z2 = 10.0000000000000000+0.0000015000000000i

Hm. It must be calling toString() without any parameters then. It's strange, I was sure I had tested it with different format strings... -Lars
Nov 18 2010