www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - toString(int) bug and two possible solutions

reply Andrew Edwards <ridimz_at yahoo.dot.com> writes:
There is a bug in the toString() function that prevents it from properly 
converting ints between '0' and '8' to their correct string value.

For example, the following program:

for(int i = 0; i <= 10; i++)
{
   printf(toString(i));
   printf(\n);
}

produces the output:

F:\>tostring
0123456789
123456789
23456789
3456789
456789
56789
6789
789
89
9
10

Obviously this is the incorrect result. The cause of this problem is 
located at line number 1501 .. 1503 of std.string.d

Which reads:

   if(u < 10)
     result = digits[u .. u+1];

Solution 1:

   if (u < 10)
     result ~= digits[u];

Solution 2:

   if(u < 10) {
     result.length = 1;
     result[0] = digits[u];
   }

Your choice really!

Regards,
Andrew
May 16 2004
next sibling parent reply Andy Friesen <andy ikagames.com> writes:
Andrew Edwards wrote:
 There is a bug in the toString() function that prevents it from properly 
 converting ints between '0' and '8' to their correct string value.
 
 For example, the following program:
 
 for(int i = 0; i <= 10; i++)
 {
   printf(toString(i));
   printf(\n);
 }

The problem is that you're treating a D string as though it were a C string. Strings are not necessarily null terminated. (string literals seem to happen to have a zero following the last char, but I'm not sure whether that's part of the spec or not) printf(toStringz(i)); should behave as expected. What would be nice is a printf overload that accepts a D string as its first argument instead of a C string. -- andy
May 16 2004
next sibling parent reply Andrew Edwards <ridimz_at yahoo.dot.com> writes:
Andy Friesen wrote:

 Andrew Edwards wrote:
 
 There is a bug in the toString() function that prevents it from 
 properly converting ints between '0' and '8' to their correct string 
 value.

 For example, the following program:

 for(int i = 0; i <= 10; i++)
 {
   printf(toString(i));
   printf(\n);
 }

The problem is that you're treating a D string as though it were a C string. Strings are not necessarily null terminated. (string literals seem to happen to have a zero following the last char, but I'm not sure whether that's part of the spec or not) printf(toStringz(i)); should behave as expected.

I'm afraid you're mistaken ma'boy! Your proposed solution results in: tostring.d(9): function toStringz (char[]string) does not match argument types (int) The other problem is that I didn't treat D string as anything other than a D string. I merely pointed out a flaw in std.string.d and provided two solutions that remedies the problem. As currently implemented the conversion does not work and needs to be patched. You find a better way of doing it and I'd simply say congratulations! Either way it needs to be fixed. Regards, Andrew
 What would be nice is a printf overload that accepts a D string as its 
 first argument instead of a C string.
 
  -- andy

May 16 2004
parent reply Andy Friesen <andy ikagames.com> writes:
Andrew Edwards wrote:
 Andy Friesen wrote:
 
 The problem is that you're treating a D string as though it were a C 
 string.  Strings are not necessarily null terminated. (string literals 
 seem to happen to have a zero following the last char, but I'm not 
 sure whether that's part of the spec or not)

 printf(toStringz(i)); should behave as expected.

I'm afraid you're mistaken ma'boy! Your proposed solution results in: tostring.d(9): function toStringz (char[]string) does not match argument types (int)

... gah. Right. You need printf("%.s", toString(i)); or printf(toStringz(toString(i)); (ew) -- andy
May 16 2004
parent Andrew Edwards <ridimz_at yahoo.dot.com> writes:
Andy Friesen wrote:
 Andrew Edwards wrote:
 
 Andy Friesen wrote:

 The problem is that you're treating a D string as though it were a C 
 string.  Strings are not necessarily null terminated. (string 
 literals seem to happen to have a zero following the last char, but 
 I'm not sure whether that's part of the spec or not)

 printf(toStringz(i)); should behave as expected.

I'm afraid you're mistaken ma'boy! Your proposed solution results in: tostring.d(9): function toStringz (char[]string) does not match argument types (int)

.... gah. Right. You need printf("%.s", toString(i)); or printf(toStringz(toString(i)); (ew)

Why should I need to do toStringz(toSring(i)) when toSring(i) already returns a D string? OK... I see the problem, I'm treating printf() as a D function vice what it realy is: a C function. Damn! We need a better IO facility. My apologies! Andrew
  -- andy

May 16 2004
prev sibling parent Andrew Edwards <ridimz_at yahoo.dot.com> writes:
Andy Friesen wrote:

 Andrew Edwards wrote:
 
 There is a bug in the toString() function that prevents it from 
 properly converting ints between '0' and '8' to their correct string 
 value.

 For example, the following program:

 for(int i = 0; i <= 10; i++)
 {
   printf(toString(i));
   printf(\n);
 }

The problem is that you're treating a D string as though it were a C string. Strings are not necessarily null terminated. (string literals seem to happen to have a zero following the last char, but I'm not sure whether that's part of the spec or not) printf(toStringz(i)); should behave as expected.

One more thing toStringz is intended to convert a D string to a null terminated C string; as such, it does not manipulate numbers. Chao, Andrew
 
 What would be nice is a printf overload that accepts a D string as its 
 first argument instead of a C string.
 
  -- andy

May 16 2004
prev sibling next sibling parent Ben Hinkle <bhinkle4 juno.com> writes:
Andrew Edwards wrote:

 There is a bug in the toString() function that prevents it from properly
 converting ints between '0' and '8' to their correct string value.
 
 For example, the following program:
 
 for(int i = 0; i <= 10; i++)
 {
    printf(toString(i));
    printf(\n);
 }
 
 produces the output:
 
 F:\>tostring
 0123456789
 123456789
 23456789
 3456789
 456789
 56789
 6789
 789
 89
 9
 10
 
 Obviously this is the incorrect result. The cause of this problem is
 located at line number 1501 .. 1503 of std.string.d
 
 Which reads:
 
    if(u < 10)
      result = digits[u .. u+1];
 
 Solution 1:
 
    if (u < 10)
      result ~= digits[u];
 
 Solution 2:
 
    if(u < 10) {
      result.length = 1;
      result[0] = digits[u];
    }
 
 Your choice really!
 
 Regards,
 Andrew

That's not a bug - it's a feature! I vaguely remember a recent thread where it came up that toString on a digit will just return a slice of the digits string - but I can't find the thread anymore. Anyway, to turn a D string into a C string make sure it is null terminated: printf(toString(i) ~ "\0"); If you want you can change std.string to slice something like "0\x001\x002\x003\x004\x005\x006\x007\x008\x009".
May 16 2004
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 16 May 2004 18:40:12 -0400, Andrew Edwards wrote:

 There is a bug in the toString() function that prevents it from properly 
 converting ints between '0' and '8' to their correct string value.
 
 For example, the following program:
 
 for(int i = 0; i <= 10; i++)
 {
    printf(toString(i));
    printf(\n);
 }
 
 produces the output:
 
 F:\>tostring
 0123456789
 123456789
 23456789
 3456789
 456789
 56789
 6789
 789
 89
 9
 10
 
 Obviously this is the incorrect result. The cause of this problem is 
 located at line number 1501 .. 1503 of std.string.d
 
 Which reads:
 
    if(u < 10)
      result = digits[u .. u+1];
 
 Solution 1:
 
    if (u < 10)
      result ~= digits[u];
 
 Solution 2:
 
    if(u < 10) {
      result.length = 1;
      result[0] = digits[u];
    }
 
 Your choice really!
 
 Regards,
 Andrew

Is it a bug? Doesn't printf() needs a C string as its first argument, and toString(int) returns a D string? What is annoying is the D compiler's failure to notice this as it does an implicit cast that does not actually convert the D string into a C string. But explictly using printf( toStringz ( toString(i) ) ) will work instead. I was hoping that printf( cast(char [])toString(i) ) might work, but that doesn't do anything either. -- Derek 17/May/04 11:13:05 AM
May 16 2004
next sibling parent Derek Parnell <derek psych.ward> writes:
On Mon, 17 May 2004 11:24:19 +1000, Derek Parnell wrote:

 On Sun, 16 May 2004 18:40:12 -0400, Andrew Edwards wrote:
 
 There is a bug in the toString() function that prevents it from properly 
 converting ints between '0' and '8' to their correct string value.
 
 For example, the following program:
 
 for(int i = 0; i <= 10; i++)
 {
    printf(toString(i));
    printf(\n);
 }
 
 produces the output:
 
 F:\>tostring
 0123456789
 123456789
 23456789
 3456789
 456789
 56789
 6789
 789
 89
 9
 10
 
 Obviously this is the incorrect result. The cause of this problem is 
 located at line number 1501 .. 1503 of std.string.d
 
 Which reads:
 
    if(u < 10)
      result = digits[u .. u+1];
 
 Solution 1:
 
    if (u < 10)
      result ~= digits[u];
 
 Solution 2:
 
    if(u < 10) {
      result.length = 1;
      result[0] = digits[u];
    }
 
 Your choice really!
 
 Regards,
 Andrew

Is it a bug? Doesn't printf() needs a C string as its first argument, and toString(int) returns a D string? What is annoying is the D compiler's failure to notice this as it does an implicit cast that does not actually convert the D string into a C string. But explictly using printf( toStringz ( toString(i) ) ) will work instead. I was hoping that printf( cast(char [])toString(i) ) might work, but that doesn't do anything either.

This seems to work too, but its a bit limiting... //------------------- import std.string; int printf(char []x) { return std.string.printf( toStringz(x)); } void main() { for(int i = 0; i <= 10; i++) { printf( toString(i)); printf(\n); }; } //------------------- -- Derek 17/May/04 11:47:22 AM
May 16 2004
prev sibling parent reply "Vathix" <vathixSpamFix dprogramming.com> writes:
 What is annoying is the D compiler's failure to notice this as it does an
 implicit cast that does not actually convert the D string into a C string.

A char* isn't necessarily a C string. When char[] converts to char* it just drops the length, like it does from int[] to int* etc.
May 16 2004
parent Derek Parnell <derek psych.ward> writes:
On Mon, 17 May 2004 00:26:24 -0400, Vathix wrote:

 What is annoying is the D compiler's failure to notice this as it does an
 implicit cast that does not actually convert the D string into a C string.

A char* isn't necessarily a C string. When char[] converts to char* it just drops the length, like it does from int[] to int* etc.

Technically you are correct. A C-string is a (char *) AND the set of characters pointed to is deemed terminated when a zero-value char is found. However a (char *) is also not a (char []) either. The first is a pointer to a char, and the second is structure whose first element is a 4-byte integer followed by zero or more char elements. I thought that when a D string (char []) is converted to a C string (char *) a new piece of RAM is allocated (length of D-string + 1) and the char elements are copied to the new RAM location and a zero-char is placed in the last RAM position. The new RAM location is returned (char *). I'd still like the compiler to tell us when its implicitly casting char[] to char* for us. Wouldn't it save many annoying buglets? Something like this should do the conversions too, IMO ... char *x; char[] y; . . . x = cast (char *)y; y = cast (char [])x; -- Derek 17/May/04 4:34:02 PM
May 16 2004