www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Arithmetic conversions and a surprise with 'max'

reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
The following program demonstrates a problem that I just hit. It is a 
known gotcha of arithmetic conversion rules.

The program is trying to center some text around an index of a char 
array. To avoid negative index values, it calls 'max' to limit the 
starting offset at 0.

import std.algorithm;

void main()
{
     // An empty line
     char[10] line = ' ';

     // We want to center some text around the first quarter mark
     int center_of_text = line.length / 4;
     string text = "01234567";

     // To be safe, we want to limit the starting index at 0.
     // (No negative index please!)
     int start = max(0, center_of_text - text.length / 2);

     assert(start >= 0); // FAILS!
}

The problem is due to converting the second argument of max to unsigned.

Ali
Jun 11 2010
next sibling parent reply div0 <div0 users.sourceforge.net> writes:
On 11/06/2010 20:00, Ali Çehreli wrote:
 The following program demonstrates a problem that I just hit. It is a
 known gotcha of arithmetic conversion rules.

 The program is trying to center some text around an index of a char
 array. To avoid negative index values, it calls 'max' to limit the
 starting offset at 0.

 import std.algorithm;

 void main()
 {
 // An empty line
 char[10] line = ' ';

 // We want to center some text around the first quarter mark
 int center_of_text = line.length / 4;
 string text = "01234567";

 // To be safe, we want to limit the starting index at 0.
 // (No negative index please!)
 int start = max(0, center_of_text - text.length / 2);

 assert(start >= 0); // FAILS!
 }

 The problem is due to converting the second argument of max to unsigned.

 Ali
It's not a 'gotcha of arithmetic conversion rules'. Write your own max function: import std.algorithm; import std.stdio; /* T max(T)(T a, T b) { if(a > b) return a; return b; }*/ void main(){ // An empty line char[10] line = ' '; // We want to center some text around the first quarter mark int center_of_text = line.length / 4; string text = "01234567"; // To be safe, we want to limit the starting index at 0. // (No negative index please!) auto v0 = 0; auto v1 = center_of_text - text.length / 2; int start = max(v0, v1); writeln(typeid(typeof(v0)).toString); writeln(typeid(typeof(v1)).toString); writefln("v1: %d", v1); writefln("start: %d", start); assert(start >= 0); // FAILS! } If you uncomment the local max you get: test.d(25): Error: template test.max(T) does not match any function template declaration test.d(25): Error: template test.max(T) cannot deduce template function from argument types !()(int,uint) There is special magic in std.algorithm.max to make it work. Arguably the implementation in std.algorithm.max is wrong. Allowing mixing of unsigned and signed seems like a bad idea to me. -- My enormous talent is exceeded only by my outrageous laziness. http://www.ssTk.co.uk
Jun 11 2010
parent div0 <div0 users.sourceforge.net> writes:
On 11/06/2010 21:02, div0 wrote:
 On 11/06/2010 20:00, Ali Çehreli wrote:
 The following program demonstrates a problem that I just hit. It is a
 known gotcha of arithmetic conversion rules.

 The program is trying to center some text around an index of a char
 array. To avoid negative index values, it calls 'max' to limit the
 starting offset at 0.

 import std.algorithm;

 void main()
 {
 // An empty line
 char[10] line = ' ';

 // We want to center some text around the first quarter mark
 int center_of_text = line.length / 4;
 string text = "01234567";

 // To be safe, we want to limit the starting index at 0.
 // (No negative index please!)
 int start = max(0, center_of_text - text.length / 2);

 assert(start >= 0); // FAILS!
 }

 The problem is due to converting the second argument of max to unsigned.

 Ali
It's not a 'gotcha of arithmetic conversion rules'.
It is an all. (though max is wrong as well) uint a; int b; b = a; Compiles cleanly. I'm going nuts, I thought that had been disallowed.
 Write your own max function:

 import std.algorithm;
 import std.stdio;

 /*
 T max(T)(T a, T b)
 {
 if(a > b)
 return a;
 return b;
 }*/

 void main(){

 // An empty line
 char[10] line = ' ';

 // We want to center some text around the first quarter mark
 int center_of_text = line.length / 4;
 string text = "01234567";

 // To be safe, we want to limit the starting index at 0.
 // (No negative index please!)
 auto v0 = 0;
 auto v1 = center_of_text - text.length / 2;
 int start = max(v0, v1);

 writeln(typeid(typeof(v0)).toString);
 writeln(typeid(typeof(v1)).toString);
 writefln("v1: %d", v1);
 writefln("start: %d", start);
 assert(start >= 0); // FAILS!
 }

 If you uncomment the local max you get:

 test.d(25): Error: template test.max(T) does not match any function
 template declaration
 test.d(25): Error: template test.max(T) cannot deduce template function
 from argument types !()(int,uint)

 There is special magic in std.algorithm.max to make it work.
 Arguably the implementation in std.algorithm.max is wrong.

 Allowing mixing of unsigned and signed seems like a bad idea to me.
-- My enormous talent is exceeded only by my outrageous laziness. http://www.ssTk.co.uk
Jun 11 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 11 Jun 2010 15:00:19 -0400, Ali Çehreli <acehreli yahoo.com> wrote:

 The following program demonstrates a problem that I just hit. It is a  
 known gotcha of arithmetic conversion rules.

 The program is trying to center some text around an index of a char  
 array. To avoid negative index values, it calls 'max' to limit the  
 starting offset at 0.

 import std.algorithm;

 void main()
 {
      // An empty line
      char[10] line = ' ';

      // We want to center some text around the first quarter mark
      int center_of_text = line.length / 4;
      string text = "01234567";

      // To be safe, we want to limit the starting index at 0.
      // (No negative index please!)
      int start = max(0, center_of_text - text.length / 2);

      assert(start >= 0); // FAILS!
 }

 The problem is due to converting the second argument of max to unsigned.
These kinds of things can typically be converted into a different form that removes the subtraction, or uses it in a safe way: int start = center_of_text - min(center_of_text, text.length / 2); -Steve
Jun 14 2010