www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - literals

reply so <so so.do> writes:
Hello, after a little discussion on D.learn, i better ask this here since  
i didn't get much replies.
Two lil questions.

Why "3" is an int?
Why "0.3" is a double?

Thanks!

-- 
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
next sibling parent reply Robert Clipsham <robert octarineparrot.com> writes:
On 28/03/10 12:02, so wrote:
 Hello, after a little discussion on D.learn, i better ask this here
 since i didn't get much replies.
 Two lil questions.

 Why "3" is an int?
 Why "0.3" is a double?

 Thanks!
This should help you understand: http://digitalmars.com/d/2.0/lex.html#integerliteral If you scroll down from there you'll also see what counts as a floating point literal and why.
Mar 28 2010
parent so <so so.do> writes:
You misunderstood the question, i didn't ask "what", i asked "why" :)

I know what are the literals.

Again the question is...
Why "3" is "especially" an "int=int32"?
Why "0.3" is "especially" a "double=float64"?

Thanks.

On Sun, 28 Mar 2010 16:08:27 +0400, Robert Clipsham  
<robert octarineparrot.com> wrote:

 On 28/03/10 12:02, so wrote:
 Hello, after a little discussion on D.learn, i better ask this here
 since i didn't get much replies.
 Two lil questions.

 Why "3" is an int?
 Why "0.3" is a double?

 Thanks!
This should help you understand: http://digitalmars.com/d/2.0/lex.html#integerliteral If you scroll down from there you'll also see what counts as a floating point literal and why.
-- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
so:
 Why "3" is an int?
 Why "0.3" is a double?
A possible answer: because D2 has no polysemus literals yet :-) Bye, bearophile
Mar 28 2010
next sibling parent reply so <so so.do> writes:
That also means, you can't write a generic code in a C derived language  
"by definition", right?

Thanks.

On Sun, 28 Mar 2010 16:30:28 +0400, bearophile <bearophileHUGS lycos.com>  
wrote:

 so:
 Why "3" is an int?
 Why "0.3" is a double?
A possible answer: because D2 has no polysemus literals yet :-) Bye, bearophile
-- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
parent reply Fawzi Mohamed <fawzi gmx.ch> writes:
On 28-mar-10, at 13:39, so wrote:

 That also means, you can't write a generic code in a C derived  
 language "by definition", right?

 Thanks.

 On Sun, 28 Mar 2010 16:30:28 +0400, bearophile <bearophileHUGS lycos.com 
 wrote:
 so:
 Why "3" is an int?
 Why "0.3" is a double?
A possible answer: because D2 has no polysemus literals yet :-)
no the answer is more complex: for integers normally the smallest possible integral type is used (but not exactly int wins in some occasions, check the exact rules) and then implicit conversion can take place. The rules are such that it should do what one means most times (so conversion to long or uint is ok), but there are some pitfalls, often associated to conversion to unsigned (that I find useful though, even if it can lead to bugs in some occasions, and there are some reasons to like infinite integers as some languages have). Still if you write a very large number it is a long for example. floating point numbers are a bit different (because they are always approximated), but the philosophy is similar. The absence polysemus literals is not a problem in my opinion, you can avoid that having good implicit casting rules. unexpected results normally happen just when you have something very ambiguous. For example if you have an overloaded function and you call it with a literal then in *any* language you need to decide which default value has the literal (to decide with which type to start before looking at implicit conversions). D (like C) chooses int for for 3, and double for 0.3 (the latter I suppose for C compatibility and efficiency reasons, because one could argue that real would be a "safer" choice). Yes there are some dark corners, but I don't see things that make generic programming impossible. Maybe if you would say what you try to do you would receive more meaningful answers. Fawzi
Mar 28 2010
parent reply so <so so.do> writes:
I don't think it is that complex, if it is please point me.
It is simple math.

"3" the number belongs to many number systems.
Making default int (actually "signed int") is the source of complexity,  
same goes for "0.3".

For function overload case:

foo(float)
foo(double)
foo(real)

If i call the overloaded functions above with foo(0.3),
since i haven't explicitly stated (in C world, i explicitly stated it is  
double) which function i refer,
it is my fault, compiler should warn/error or give me the highest  
precision available, you name it.
The reason might be compatibility but i am unable to see any efficiency  
there.

What i want is simple, in a generic code, which in this case a template,
i should be able to write the code below with "zero" implicit casts.

T inv(T m)() {
	return 1.0 / m;
}

What do you see in this code? What the user wants?
User explicitly stated that he wants "1.0" in the type of T.
So when "T" is float, give the float version of "1.0".
If i am not mistaken, this is actually how constant folding actually  
works, see the page.

Thanks.

On Sun, 28 Mar 2010 17:20:41 +0400, Fawzi Mohamed <fawzi gmx.ch> wrote:

 no the answer is more complex:
 for integers normally the smallest possible integral type is used (but  
 not exactly int wins in some occasions, check the exact rules) and then  
 implicit conversion can take place.
 The rules are such that it should do what one means most times (so  
 conversion to long or uint is ok), but there are some pitfalls, often  
 associated to conversion to unsigned (that I find useful though, even if  
 it can lead to bugs in some occasions, and there are some reasons to  
 like infinite integers as some languages have).
 Still if you write a very large number it is a long for example.

 floating point numbers are a bit different (because they are always  
 approximated), but the philosophy is similar.

 The absence polysemus literals is not a problem in my opinion, you can  
 avoid that having good implicit casting rules.

 unexpected results normally happen just when you have something very  
 ambiguous.
 For example if you have an overloaded function and you call it with a  
 literal then in *any* language you need to decide which default value  
 has the literal (to decide with which type to start before looking at  
 implicit conversions).
 D (like C) chooses int for for 3, and double for 0.3 (the latter I  
 suppose for C compatibility and efficiency reasons, because one could  
 argue that real would be a "safer" choice).

 Yes there are some dark corners, but I don't see things that make  
 generic programming impossible.
 Maybe if you would say what you try to do you would receive more  
 meaningful answers.

 Fawzi
-- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
next sibling parent reply so <so so.do> writes:
One more thing about the code :

T inv(T m)() {
	return 1.0 / m;
}

What if we call inv(5)?
This is an error, why?

- Function explicitly states that the parameter is a floating point type.
- Operations between floating point types and integers result a floating  
point type
, as you see from the return type, code returns the type of parameter, in  
this case an integer.

Thanks.
Mar 28 2010
parent reply Fawzi Mohamed <fawzi gmx.ch> writes:
On 28-mar-10, at 15:03, so wrote:

 One more thing about the code :

 T inv(T m)() {
 	return 1.0 / m;
 }

 What if we call inv(5)?
yes what about it what should the compiler do? read your mind? the code that I gave returns 0
 This is an error, why?

 - Function explicitly states that the parameter is a floating point  
 type.
how?
 - Operations between floating point types and integers result a  
 floating point type
this is the case
 , as you see from the return type, code returns the type of  
 parameter, in this case an integer.
yes you can write a template that automatically "upconverts" to float, but I don't think that is the correct thing for a default thing to do, why do you alway want to have floats as result?
 Thanks.
Mar 28 2010
parent reply so <so so.do> writes:
On Sun, 28 Mar 2010 18:18:20 +0400, Fawzi Mohamed <fawzi gmx.ch> wrote:

 how?
When you divide 1.0 / 5, compilers warns or implicitly converts "5" to a floating point type, am i wrong? I told you what i want, remember "zero implicit cast"
 yes you can write a template that automatically "upconverts" to float,  
 but I don't think that is the correct thing for a default thing to do,  
 why do you alway want to have floats as result?
I don't always want float as result, but check the function again. What happens there? "1.0 / m" which will result a floating point type, what code suggests is, that this type will be "T" -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 28 Mar 2010 09:26:43 -0400, so <so so.do> wrote:

 On Sun, 28 Mar 2010 18:18:20 +0400, Fawzi Mohamed <fawzi gmx.ch> wrote:

 how?
When you divide 1.0 / 5, compilers warns or implicitly converts "5" to a floating point type, am i wrong? I told you what i want, remember "zero implicit cast"
 yes you can write a template that automatically "upconverts" to float,  
 but I don't think that is the correct thing for a default thing to do,  
 why do you alway want to have floats as result?
I don't always want float as result, but check the function again. What happens there? "1.0 / m" which will result a floating point type, what code suggests is, that this type will be "T"
What you want is implicit function template instantiation based on the return type. D doesn't do that, it doesn't even allow overloading based on the return type. However, you can infer the return type using auto: auto inv(T)(T m) { return 1.0/m; } -Steve
Mar 28 2010
parent reply so <so so.do> writes:
 What you want is implicit function template instantiation based on the  
 return type.  D doesn't do that, it doesn't even allow overloading based  
 on the return type.  However, you can infer the return type using auto:

 auto inv(T)(T m)
 {
     return 1.0/m;
 }

 -Steve
Hello! I guess, i am unable to express myself. In code : T inv(T)(T m) { return 1.0/m; } If we forget the rules of default literals that C derived languages have, just for a second. And enforce our own little rule. - no implicit casts With this in mind this code says; i gave you T, give me T in return, perfectly clear i guess? Now when you call the function with a floating point, real, double, float..., you will get what you asked for. Now what about other types? Say you call it with a non floating type, when compiler tries to divide the first thing it will encounter that one of two elements of the operation is a non-float, since we enforced a rule, it gives warning or error. Thanks! -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 28 Mar 2010 14:29:52 -0400, so <so so.do> wrote:

 What you want is implicit function template instantiation based on the  
 return type.  D doesn't do that, it doesn't even allow overloading  
 based on the return type.  However, you can infer the return type using  
 auto:

 auto inv(T)(T m)
 {
     return 1.0/m;
 }

 -Steve
Hello! I guess, i am unable to express myself. In code : T inv(T)(T m) { return 1.0/m; } If we forget the rules of default literals that C derived languages have, just for a second. And enforce our own little rule. - no implicit casts With this in mind this code says; i gave you T, give me T in return, perfectly clear i guess? Now when you call the function with a floating point, real, double, float..., you will get what you asked for. Now what about other types? Say you call it with a non floating type, when compiler tries to divide the first thing it will encounter that one of two elements of the operation is a non-float, since we enforced a rule, it gives warning or error.
What you are asking for is a template instantiation that depends on instantiating itself. How about a function like this? void foo(int x); T inv(T)(T m) { foo(m); return 1.0/m; } How do you know what type to use for T? It's simple when you make inv a one-liner, there is only one thing to look at. But as you start making the function more complex, then it's impossible to tell what the user actually wants. In this instance, T must be implicitly convertable to int, but also must be a floating point type. Clearly there is an error here, but where is it? This is called ambiguity, and to avoid it, the compiler makes a decision, right or wrong, based on the literal used to call the function. It expects you, the user, to help out when it doesn't make the right one. I don't think it's too much to ask. It's simply a tradeoff between being an all-powerful oracle-like compiler and being a simple-to-write compiler. What the compiler sees when deciding to instantiate is this: T inv(T)(T m) {...} And it makes a decision. If you have a problem with writing inv(5.0) instead of inv(5), then maybe D just isn't for you. -Steve
Mar 28 2010
parent reply so <so so.do> writes:
On Sun, 28 Mar 2010 23:46:55 +0400, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:

 On Sun, 28 Mar 2010 14:29:52 -0400, so <so so.do> wrote:

 What you want is implicit function template instantiation based on the  
 return type.  D doesn't do that, it doesn't even allow overloading  
 based on the return type.  However, you can infer the return type  
 using auto:

 auto inv(T)(T m)
 {
     return 1.0/m;
 }

 -Steve
Hello! I guess, i am unable to express myself. In code : T inv(T)(T m) { return 1.0/m; } If we forget the rules of default literals that C derived languages have, just for a second. And enforce our own little rule. - no implicit casts With this in mind this code says; i gave you T, give me T in return, perfectly clear i guess? Now when you call the function with a floating point, real, double, float..., you will get what you asked for. Now what about other types? Say you call it with a non floating type, when compiler tries to divide the first thing it will encounter that one of two elements of the operation is a non-float, since we enforced a rule, it gives warning or error.
What you are asking for is a template instantiation that depends on instantiating itself. How about a function like this? void foo(int x); T inv(T)(T m) { foo(m); return 1.0/m; } How do you know what type to use for T? It's simple when you make inv a one-liner, there is only one thing to look at. But as you start making the function more complex, then it's impossible to tell what the user actually wants. In this instance, T must be implicitly convertable to int, but also must be a floating point type. Clearly there is an error here, but where is it? This is called ambiguity, and to avoid it, the compiler makes a decision, right or wrong, based on the literal used to call the function. It expects you, the user, to help out when it doesn't make the right one. I don't think it's too much to ask. It's simply a tradeoff between being an all-powerful oracle-like compiler and being a simple-to-write compiler.
I don't think a language/compiler should make premature decisions when an ambiguity happens, What is warning or error mechanism for then? In your case, we have a rule at hand, and you broke it. That should require an explicit cast, Also, when you have a framework, you would have the T version of the "foo" right? I guess i am missing something here.
 What the compiler sees when deciding to instantiate is this:

 T inv(T)(T m) {...}

 And it makes a decision.
I am perfectly fine with that.
 If you have a problem with writing inv(5.0) instead of inv(5), then  
 maybe D just isn't for you.

 -Steve
I am not sure what you mean really? You mean the other way around? i want : inv(5.0f), inv(5.0d), inv(5.0L) or T x; inv(x); // where T is floating point type. Thanks. -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 28 Mar 2010 15:09:35 -0400, so <so so.do> wrote:

 On Sun, 28 Mar 2010 23:46:55 +0400, Steven Schveighoffer  
 <schveiguy yahoo.com> wrote:
 What you are asking for is a template instantiation that depends on  
 instantiating itself.  How about a function like this?

 void foo(int x);

 T inv(T)(T m) {
     foo(m);
     return 1.0/m;
 }

 How do you know what type to use for T?  It's simple when you make inv  
 a one-liner, there is only one thing to look at.  But as you start  
 making the function more complex, then it's impossible to tell what the  
 user actually wants.   In this instance, T must be implicitly  
 convertable to int, but also must be a floating point type.  Clearly  
 there is an error here, but where is it?  This is called ambiguity, and  
 to avoid it, the compiler makes a decision, right or wrong, based on  
 the literal used to call the function.  It expects you, the user, to  
 help out when it doesn't make the right one.  I don't think it's too  
 much to ask.  It's simply a tradeoff between being an all-powerful  
 oracle-like compiler and being a simple-to-write compiler.
I don't think a language/compiler should make premature decisions when an ambiguity happens, What is warning or error mechanism for then?
It has to make a decision on literals, or else you can't use them without casting. I'd hate to write code for a compiler that requires you to cast every literal.
 In your case, we have a rule at hand, and you broke it. That should  
 require an explicit cast,
 Also, when you have a framework, you would have the T version of the  
 "foo" right? I guess i am missing something here.
What you are missing is it's easy to look at a simple example and conclude that a compiler should have enough intelligence to understand what you meant in that simple example. However, you must also look at more complex cases and figure out if the rule holds for those as well. I think in your simple one-liner, it is obvious to a person that inv only compiles for floating point types. However, you want it to work for the literal "5", right? So you want the compiler to use the code of the function to deduce what the type of 5 is, knowing that the function should compile for floating point types. It just isn't a workable general solution. Code can be very complex, and to require the compiler to understand the semantic meaning of the code in this way is not how the D compiler is built. It is *told* how to compile something, and then checks that what you told it to do makes sense. It doesn't figure stuff out for itself in anything bigger than a single statement. D is supposed to be a context-free grammar.
 What the compiler sees when deciding to instantiate is this:

 T inv(T)(T m) {...}

 And it makes a decision.
I am perfectly fine with that.
I am confused. I thought this was your problem? That it wasn't taking the body of the function into account when deciding the type of T?
 If you have a problem with writing inv(5.0) instead of inv(5), then  
 maybe D just isn't for you.

 -Steve
I am not sure what you mean really? You mean the other way around? i want : inv(5.0f), inv(5.0d), inv(5.0L) or T x; inv(x); // where T is floating point type.
This works today, no (except for the d is redundant)? I guess I don't really understand what you are looking for... -Steve
Mar 29 2010
next sibling parent Fawzi Mohamed <fawzi gmx.ch> writes:
	charset=US-ASCII;
	format=flowed;
	delsp=yes
Content-Transfer-Encoding: 7bit


On 29-mar-10, at 12:57, Steven Schveighoffer wrote:

 On Sun, 28 Mar 2010 15:09:35 -0400, so <so so.do> wrote:

 On Sun, 28 Mar 2010 23:46:55 +0400, Steven Schveighoffer <schveiguy yahoo.com 
 wrote:
[...] I am not sure what you mean really? You mean the other way around? i want : inv(5.0f), inv(5.0d), inv(5.0L) or T x; inv(x); // where T is floating point type.
This works today, no (except for the d is redundant)? I guess I don't really understand what you are looking for...
I guess I am not alone then :)
Mar 29 2010
prev sibling parent reply so <so so.do> writes:
 This works today, no (except for the d is redundant)?  I guess I don't  
 really understand what you are looking for...

 -Steve
Could you please write the equivalent D code for generic inv function? I guess it would be T inv(T)(T m) { return 1 / m; } Right? With a glance, It is perfectly what i want! Now lets apply C derived language (D is one of them as we all know) rules here and analyze what is happening. Q - what is "1" ? A - "1" is an "int", which means, "signed int", also in D it is "32 bit signed it", in C bit count is implementation defined, of course D way much better. Q - what happens when i call inv(5.0f) A - "return float(1) / 5.0f" Anything wrong with questions and answers here? What i am asking is why it have to be this way. In "return 1 / float", why 1 is assumed as an integer, and consequently casted to float. In math [real numbers] > [integers] > [natural numbers}, why 1 is an integer? and especially why 1 is an 32 bit integer? T foo(T)(T k) { return k + 3; } General answer will be like, above code should fricking work, without my explicit cast! And you are right, That is perfectly fine with me! :) I again ask, for this code to work, Is it necessary "3" be a 32bit integer, or a native type at all? -- You are right, inv is a dumb function, we need a real world example. Say we have a function with 2 overloads, which might be the reasons of precision, performance... float foo(float m) {} double foo(double m) {} Now we need to call this within a generic code. void test(T)(T m) { return m * foo(0.3); } Since 0.3 is a double, double version will be called, so we should use foo(cast(T)0.3), right? When the function foo is a popular one, what am i going to do? anything other than casting at each occurrence? with functions like foo, code becomes like... void test2(T)(T m) { T n = foo1(cast(T)0.3) / foo2(cast(T)9); .... ... } And everyone thinks that he is writing generic code... Thanks! -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 29 2010
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 29 Mar 2010 09:20:56 -0400, so <so so.do> wrote:

 You are right, inv is a dumb function, we need a real world example.
 Say we have a function with 2 overloads, which might be the reasons of  
 precision, performance...

 float foo(float m) {}
 double foo(double m) {}

 Now we need to call this within a generic code.

 void test(T)(T m) {
 	return m * foo(0.3);
 }

 Since 0.3 is a double, double version will be called, so we should use  
 foo(cast(T)0.3), right?
 When the function foo is a popular one, what am i going to do? anything  
 other than casting at each occurrence?
 with functions like foo, code becomes like...

 void test2(T)(T m) {
 	T n = foo1(cast(T)0.3) / foo2(cast(T)9);
 	....
 	...
 }
OK, I see the issue now. The syntax for declaring that a literal is the type of T is too verbose. This is different than what I thought you wanted. I agree this isn't the best situation. It's good that it is possible, but we should strive to find a better way. Sorry for the noise from misunderstanding. As for the better way, one possibility is using C++-style value type constructors: float(0.3) This provides a way to have T be a user-defined type as well (define static opCall or a constructor) So the test function would look like this: void test(T)(T m) { { return m * foo(T(0.3)); } It would be nice to say "in this function, assume numeric literals are of type T," but that might be too specific a solution (could only apply to builtin types). I don't think it's feasible for the compiler to infer what type it should use. -Steve
Mar 29 2010
parent reply so <so so.do> writes:
 It would be nice to say "in this function, assume numeric literals are  
 of type T," but that might be too specific a solution (could only apply  
 to builtin types).  I don't think it's feasible for the compiler to  
 infer what type it should use.

 -Steve
It would be nice indeed! My proposal"ish" idea was exactly for this problem, that removing "default literal" rule, and treating every implicit constant (constant without literal) as a generic type. I don't think it will ever happen even if i was able to provide full parsing framework, since C is a strong opponent :) Thanks! -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 29 2010
parent reply Fawzi Mohamed <fawzi gmx.ch> writes:
 What i am asking is why it have to be this way.
 In "return 1 / float", why 1 is assumed as an integer, and  
 consequently casted to float.
 In math [real numbers] > [integers] > [natural numbers}, why 1 is an  
 integer? and especially why 1 is an 32 bit integer?

 T foo(T)(T k) {
 	return k + 3;
 }
 General answer will be like, above code should fricking work,  
 without my explicit cast! And you are right, That is perfectly fine  
 with me! :)
 I again ask, for this code to work, Is it necessary "3" be a 32bit  
 integer, or a native type at all?
well as you said in mathematics a Natural number is also an Integer which is also a Rational which is also a Real. One could say that you have an injection, an implicit cast from Natural to Integer to Rational to Real. By the way maybe you fear that the implicit cast is done at runtime, or has some hidden cost, but that is not the case, for literals it is done at compile time. In reality (and also in mathematics if you use cyclic groups, or approximate numbers) the situation becomes a lot more murky, but in the end not much changes. On 29-mar-10, at 15:47, so wrote:
 It would be nice to say "in this function, assume numeric literals  
 are of type T," but that might be too specific a solution (could  
 only apply to builtin types).  I don't think it's feasible for the  
 compiler to infer what type it should use.

 -Steve
It would be nice indeed! My proposal"ish" idea was exactly for this problem, that removing "default literal" rule, and treating every implicit constant (constant without literal) as a generic type. I don't think it will ever happen even if i was able to provide full parsing framework, since C is a strong opponent :)
The example you give cannot be solved easily and efficiently (find the type to use in a function) without some kind of inference based on the return type, annotations, or Hindley Milner style type inference. annotation don't scale, using inference based on the return type is very difficult and not doable in general, few languages do it (aldor for example did it), Hindley Milner is incompatible with C. Please note that (possibly due to my C background) I like to put some type annotations, in my (limited) experience that pays off also with ML style languages, otherwise when you have ambiguity small errors can change the called functions and give surprising results. Fawzi
Mar 29 2010
parent reply so <so so.do> writes:
 The example you give cannot be solved easily and efficiently (find the  
 type to use in a function) without some kind of inference based on the  
 return type, annotations, or Hindley Milner style type inference.
 annotation don't scale, using inference based on the return type is very  
 difficult and not doable in general, few languages do it (aldor for  
 example did it), Hindley Milner is incompatible with C.

 Please note that (possibly due to my C background) I like to put some  
 type annotations, in my (limited) experience that pays off also with ML  
 style languages, otherwise when you have ambiguity small errors can  
 change the called functions and give surprising results.

 Fawzi
Finally i am able to explain it with 2 words, Hindley Milner :) I want credit too! i also found it independently! Thanks for the explanation, i don't really like non-system, non-generic languages, non-D like languages. Especially when a language has dumb reasons for not providing an utility. (Java operator overloading case) D is the best choice for me by miles and i can learn to live with casts as i did in C++. -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 29 2010
parent Fawzi Mohamed <fawzi gmx.ch> writes:
On 29-mar-10, at 16:57, so wrote:

 The example you give cannot be solved easily and efficiently (find  
 the type to use in a function) without some kind of inference based  
 on the return type, annotations, or Hindley Milner style type  
 inference.
 annotation don't scale, using inference based on the return type is  
 very difficult and not doable in general, few languages do it  
 (aldor for example did it), Hindley Milner is incompatible with C.

 Please note that (possibly due to my C background) I like to put  
 some type annotations, in my (limited) experience that pays off  
 also with ML style languages, otherwise when you have ambiguity  
 small errors can change the called functions and give surprising  
 results.

 Fawzi
Finally i am able to explain it with 2 words, Hindley Milner :) I want credit too! i also found it independently!
ehm actually Hindley Milner is not enough for what you requested, it can find types of functions, but integers are still separated from floats. It can't cope well with incompatible overloads or conversions (they have type a->b)... what you want is really return type based inference something that is hard, as said as far as I know only aldor did that
 Thanks for the explanation, i don't really like non-system, non- 
 generic languages, non-D like languages.
 Especially when a language has dumb reasons for not providing an  
 utility. (Java operator overloading case)
 D is the best choice for me by miles and i can learn to live with  
 casts as i did in C++.

 -- 
 Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 29 2010
prev sibling parent reply Fawzi Mohamed <fawzi gmx.ch> writes:
On 28-mar-10, at 14:48, so wrote:

 I don't think it is that complex, if it is please point me.
 It is simple math.

 "3" the number belongs to many number systems.
 Making default int (actually "signed int") is the source of  
 complexity, same goes for "0.3".

 For function overload case:

 foo(float)
 foo(double)
 foo(real)

 If i call the overloaded functions above with foo(0.3),
 since i haven't explicitly stated (in C world, i explicitly stated  
 it is double) which function i refer,
 it is my fault, compiler should warn/error or give me the highest  
 precision available, you name it.
or give you the "default" precision. And note that this problem is not what you pointed out initially, but something I gave. I don't see the same problem with signed int...
 The reason might be compatibility but i am unable to see any  
 efficiency there.
double occupies less space and can use SSE instructions.
 What i want is simple, in a generic code, which in this case a  
 template,
 i should be able to write the code below with "zero" implicit casts.

 T inv(T m)() {
 	return 1.0 / m;
 }

 What do you see in this code?
invalid code?
 What the user wants?
probably T inv(T)(T m) { return 1 / m; }
 User explicitly stated that he wants "1.0" in the type of T.
 So when "T" is float, give the float version of "1.0".
Integers are the only values that can be represented without any approximation (unless you resort to rationals, or inefficient representations that are probably ok only at compile time or in very specific applications). So generic code should use integers, not floating points. You might argue that the way to get a floating point literal of type T is ugly: cast(T)1.0 for example fortran uses 1.0_T, but one can definitely live with it, anyway normally you should use integers, generic floating point literals is not really something that is so well defined...
 If i am not mistaken, this is actually how constant folding actually  
 works, see the page.

 Thanks.

 On Sun, 28 Mar 2010 17:20:41 +0400, Fawzi Mohamed <fawzi gmx.ch>  
 wrote:

 no the answer is more complex:
 for integers normally the smallest possible integral type is used  
 (but not exactly int wins in some occasions, check the exact rules)  
 and then implicit conversion can take place.
 The rules are such that it should do what one means most times (so  
 conversion to long or uint is ok), but there are some pitfalls,  
 often associated to conversion to unsigned (that I find useful  
 though, even if it can lead to bugs in some occasions, and there  
 are some reasons to like infinite integers as some languages have).
 Still if you write a very large number it is a long for example.

 floating point numbers are a bit different (because they are always  
 approximated), but the philosophy is similar.

 The absence polysemus literals is not a problem in my opinion, you  
 can avoid that having good implicit casting rules.

 unexpected results normally happen just when you have something  
 very ambiguous.
 For example if you have an overloaded function and you call it with  
 a literal then in *any* language you need to decide which default  
 value has the literal (to decide with which type to start before  
 looking at implicit conversions).
 D (like C) chooses int for for 3, and double for 0.3 (the latter I  
 suppose for C compatibility and efficiency reasons, because one  
 could argue that real would be a "safer" choice).

 Yes there are some dark corners, but I don't see things that make  
 generic programming impossible.
 Maybe if you would say what you try to do you would receive more  
 meaningful answers.

 Fawzi
-- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
parent reply so <so so.do> writes:
 or give you the "default" precision. And note that this problem is not  
 what you pointed out initially, but something I gave.
That is it, whole point, what the heck is a default literal or precision in a generic code?
 double occupies less space and can use SSE instructions.
float faster and use half of double, that is not a valid argument, and it is not a valid answer to the problem at hand. If i want double i must state it "0.5d", float "0.5f" real "0.5L", we are talking about again "generic" code. There are too many implicit casts, main reason is because there are default literals, am i wrong?
 invalid code?
 probably

 T inv(T)(T m) {
 	return 1 / m;
 }
Now you are just doing it for the sake of argument. :P This is another function, for another purpose.
 Integers are the only values that can be represented without any  
 approximation (unless you resort to rationals, or inefficient  
 representations that are probably ok only at compile time or in very  
 specific applications).
 So generic code should use integers, not floating points.
 You might argue that the way to get a floating point literal of type T  
 is ugly:
 	cast(T)1.0
 for example fortran uses 1.0_T, but one can definitely live with it,  
 anyway normally you should use integers, generic floating point literals  
 is not really something that is so well defined...
This is not only about floating point types, same applies to integers. 3 is default "int" right? Imagine what happens if it wasn't. ushort m0 = 30000000000000000000000000000000000; // error ubyte m1 = 3; // no implicit cast, fine byte m2 = 3; // same short m3 = 3; // same ubyte m5 = -3; // error ushort m6 = -3; // error And for the floating point case : float m = 0.5; double n = 0.5; real r = 0.5; What is wrong with this? Thanks. Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
parent reply Fawzi Mohamed <fawzi gmx.ch> writes:
On 28-mar-10, at 15:44, so wrote:

 or give you the "default" precision. And note that this problem is  
 not what you pointed out initially, but something I gave.
That is it, whole point, what the heck is a default literal or precision in a generic code?
the one to use when things are ambiguos
 double occupies less space and can use SSE instructions.
float faster and use half of double, that is not a valid argument, and it is not a valid answer to the problem at hand. If i want double i must state it "0.5d", float "0.5f" real "0.5L", we are talking about again "generic" code. There are too many implicit casts, main reason is because there are default literals, am i wrong?
 invalid code?
 probably

 T inv(T)(T m) {
 	return 1 / m;
 }
Now you are just doing it for the sake of argument. :P This is another function, for another purpose.
really the compiler cannot parse your function, and I can't either, to me what you meant is the code I wrote.
 Integers are the only values that can be represented without any  
 approximation (unless you resort to rationals, or inefficient  
 representations that are probably ok only at compile time or in  
 very specific applications).
 So generic code should use integers, not floating points.
 You might argue that the way to get a floating point literal of  
 type T is ugly:
 	cast(T)1.0
 for example fortran uses 1.0_T, but one can definitely live with  
 it, anyway normally you should use integers, generic floating point  
 literals is not really something that is so well defined...
This is not only about floating point types, same applies to integers. 3 is default "int" right? Imagine what happens if it wasn't. ushort m0 = 30000000000000000000000000000000000; // error ubyte m1 = 3; // no implicit cast, fine byte m2 = 3; // same short m3 = 3; // same ubyte m5 = -3; // error ushort m6 = -3; // error
I really don't understand your point that is *exactly* what the compiler does...
 And for the floating point case :

 float m = 0.5;
 double n = 0.5;
 real r = 0.5;

 What is wrong with this?
nothing and the compiler accepts it... It seems to me that you don't even know the language, and you want find "problems" in it that aren't there. Your code is invalid, I gave a valid code, which I think solves the problems you are trying to illustrate, and argued about the pitfalls of "generic float literals" (no language that I know of has good support for them, and that in D you would write with cast(T)literal). In D the return type of a function (like in many other languages) is determined by its arguments, so it makes no sense to having it determined from operations in its inside, like 1.0/m, T is determined *before* the call and independently from what happens inside the function
Mar 28 2010
parent reply so <so so.do> writes:
 the one to use when things are ambiguos
What ambiguity you are talking about? It is generic code, everything known.
 really the compiler cannot parse your function, and I can't either, to  
 me what you meant is the code I wrote.
So you are saying a compiler is unable to parse this : T inv(T)(T m) { return 1.0 / m; } And perfectly fine with parsing this one? T inv(T)(T m) { return 1 / m; } A coder who has the basic knowledge of "you can't take an inverse of an integer" wouldn't write the second code, and when he does, he should understand if the second code pops the error when he pass a floating point argument, inv(5.0) Why? Remember the rule again, no implicit casts.
 nothing and the compiler  accepts it...
Now you don't even read what i write, what is the point replying then, move on. :)
 It seems to me that you don't even know the language, and you want find  
 "problems" in it that aren't there.

 Your code is invalid, I gave a valid code, which I think solves the  
 problems you are trying to illustrate, and argued about the pitfalls of  
 "generic float literals" (no language that I know of has good support  
 for them, and that in D you would write with cast(T)literal).

 In D the return type of a function (like in many other languages) is  
 determined by its arguments, so it makes no sense to having it  
 determined from operations in its inside, like 1.0/m, T is determined  
 *before* the call and independently from what happens inside the function
I am not sure if i should answer you with the attitude of yours but lets have it. T is determined before the call, did i say anything against or i was the one proposing improvement over it? :) T is determined before the call yes, when i call inv(4.0f) it will be "1.0f/4.0f", with "zero implicit casts" in your case it will be "1/4.0f" which is either implicit cast or an error/warning. Thanks. -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
parent reply Fawzi Mohamed <fawzi gmx.ch> writes:
On 28-mar-10, at 16:22, so wrote:

 the one to use when things are ambiguos
What ambiguity you are talking about? It is generic code, everything known.
right f(1) with overloaded function f is not ambiguous.
 really the compiler cannot parse your function, and I can't either,  
 to me what you meant is the code I wrote.
So you are saying a compiler is unable to parse this : T inv(T)(T m) { return 1.0 / m; } [...] I am not sure if i should answer you with the attitude of yours but lets have it.
you know with someone that uses a clearly fake email, does not bother to actually check if the compiler doesn't by chance already do what he would like to have, and the reason he bashes D, says "I don't want implicit casts, because otherwise you know what I would like to have happens, and besides cast is a nasty thing, so implicit cast must be bad or something, and the name doesn't sound good... so you have to abide my rule of no implicit cast" well that looks like a troll, so as superdan says "u don't reason with'em trollz." still sometime you have to answer them just so that outsiders realizes that those things are just FUD, and because sometime there is really someone that wants to meaningfully contribute to the discussions, but starts out in a bad way... D numerical system is not perfect, and there are possible improvements, for example I think that it could learn a couple of things from fortran 90 numerical system (which mostly could be done with templates), but it is definitely very usable. goodbye Fawzi
Mar 28 2010
parent so <so so.do> writes:
Just wow...

On Sun, 28 Mar 2010 21:23:00 +0400, Fawzi Mohamed <fawzi gmx.ch> wrote:

 On 28-mar-10, at 16:22, so wrote:

 the one to use when things are ambiguos
What ambiguity you are talking about? It is generic code, everything known.
right f(1) with overloaded function f is not ambiguous.
 really the compiler cannot parse your function, and I can't either, to  
 me what you meant is the code I wrote.
So you are saying a compiler is unable to parse this : T inv(T)(T m) { return 1.0 / m; } [...] I am not sure if i should answer you with the attitude of yours but lets have it.
you know with someone that uses a clearly fake email, does not bother to actually check if the compiler doesn't by chance already do what he would like to have, and the reason he bashes D, says "I don't want implicit casts, because otherwise you know what I would like to have happens, and besides cast is a nasty thing, so implicit cast must be bad or something, and the name doesn't sound good... so you have to abide my rule of no implicit cast" well that looks like a troll, so as superdan says "u don't reason with'em trollz." still sometime you have to answer them just so that outsiders realizes that those things are just FUD, and because sometime there is really someone that wants to meaningfully contribute to the discussions, but starts out in a bad way... D numerical system is not perfect, and there are possible improvements, for example I think that it could learn a couple of things from fortran 90 numerical system (which mostly could be done with templates), but it is definitely very usable. goodbye Fawzi
-- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
prev sibling parent Justin Johansson <no spam.com> writes:
bearophile Wrote:

 so:
 Why "3" is an int?
 Why "0.3" is a double?
A possible answer: because D2 has no polysemus literals yet :-) Bye, bearophile
You meant "polysemous", right? :-) Cheers Justin
Mar 28 2010
prev sibling parent reply so <so so.do> writes:
What is interesting about it is, DMD already has the solution, and it is  
there, implemented!

Just check the page :
http://www.digitalmars.com/d/2.0/float.html

Read the constant folding.
If you already have this capabilities, why limit the user?
C was a great language but lets get over with this backward-support  
paranoia. :)

Thanks.

On Sun, 28 Mar 2010 15:02:25 +0400, so <so so.do> wrote:

 Hello, after a little discussion on D.learn, i better ask this here  
 since i didn't get much replies.
 Two lil questions.

 Why "3" is an int?
 Why "0.3" is a double?

 Thanks!
-- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
parent reply Don <nospam nospam.com> writes:
so wrote:
 What is interesting about it is, DMD already has the solution, and it is 
 there, implemented!
 
 Just check the page :
 http://www.digitalmars.com/d/2.0/float.html
 
 Read the constant folding.
 If you already have this capabilities, why limit the user?
It's a bit surprising how D treats numeric literals. Although (say) 2.1 is of type double, 2.1f is of type float, and 2.1L is of type real, they all have exactly the same value. (Not entirely true. Oddly, the maximum exponent value does seem to be limited. I think that's something which should change, as I don't think it makes much sense). The type only affects the type of expressions involving that value, it doesn't change the value. Range propagation isn't fully implemented in DMD2 yet. Once it's in, the situation will improve a little more. It's already much better than the situation in C or C++. But I agree that it's still not perfect.
 C was a great language but lets get over with this backward-support 
 paranoia. :)
 
 Thanks.
 
 On Sun, 28 Mar 2010 15:02:25 +0400, so <so so.do> wrote:
 
 Hello, after a little discussion on D.learn, i better ask this here 
 since i didn't get much replies.
 Two lil questions.

 Why "3" is an int?
 Why "0.3" is a double?

 Thanks!
Mar 28 2010
parent reply so <so so.do> writes:
Hello Don, finally!

It is hard to explain yourself when you don't know the people you talk  
have numeric coding background.
Since we know you have done much numeric and generic coding, it will be  
enough for me if you understand what i mean/ask/want,
and say what i am talking is ridiculous, or think that i am trolling, just  
say so and i shut up! :)

Thanks.

On Sun, 28 Mar 2010 23:34:43 +0400, Don <nospam nospam.com> wrote:

 so wrote:
 What is interesting about it is, DMD already has the solution, and it  
 is there, implemented!
  Just check the page :
 http://www.digitalmars.com/d/2.0/float.html
  Read the constant folding.
 If you already have this capabilities, why limit the user?
It's a bit surprising how D treats numeric literals. Although (say) 2.1 is of type double, 2.1f is of type float, and 2.1L is of type real, they all have exactly the same value. (Not entirely true. Oddly, the maximum exponent value does seem to be limited. I think that's something which should change, as I don't think it makes much sense). The type only affects the type of expressions involving that value, it doesn't change the value. Range propagation isn't fully implemented in DMD2 yet. Once it's in, the situation will improve a little more. It's already much better than the situation in C or C++. But I agree that it's still not perfect.
 C was a great language but lets get over with this backward-support  
 paranoia. :)
  Thanks.
  On Sun, 28 Mar 2010 15:02:25 +0400, so <so so.do> wrote:

 Hello, after a little discussion on D.learn, i better ask this here  
 since i didn't get much replies.
 Two lil questions.

 Why "3" is an int?
 Why "0.3" is a double?

 Thanks!
-- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
parent reply Don <nospam nospam.com> writes:
so wrote:
 Hello Don, finally!
 
 It is hard to explain yourself when you don't know the people you talk 
 have numeric coding background.
 Since we know you have done much numeric and generic coding, it will be 
 enough for me if you understand what i mean/ask/want,
 and say what i am talking is ridiculous, or think that i am trolling, 
 just say so and i shut up! :)
You're definitely not trolling! I'm not 100% sure of which issue you're referring too, but I'm aware of a few, for example: (1) Converting a floating point literal into a double literal is usually not lossless. 0.5f, 0.5, and 0.5L are all exactly the same number, since they are exactly representable. But 0.1 is not the same as 0.1L. So it's a bit odd that this silent lossless conversion is taking place. It does have a very strong precedent from C, however. (2) The interaction between implicit casting and template parameters is quite poor. Eg, the fact that '0' is an int, not a floating point type, means that something simple like: add(T)(T x) if (isFloatingPoint!(T)) doesn't work properly. It is not the same as: add(real x) since it won't allow add(0). Which is pretty annoying. Why can't 0 just mean zero???
Mar 28 2010
next sibling parent reply superdan <super dan.org> writes:
Don Wrote:

 so wrote:
 Hello Don, finally!
 
 It is hard to explain yourself when you don't know the people you talk 
 have numeric coding background.
 Since we know you have done much numeric and generic coding, it will be 
 enough for me if you understand what i mean/ask/want,
 and say what i am talking is ridiculous, or think that i am trolling, 
 just say so and i shut up! :)
You're definitely not trolling! I'm not 100% sure of which issue you're referring too, but I'm aware of a few, for example:
he wanna 1.0 to adapt to T. i'm sayin' if u pass float to inv 1.0 gotta be 10.F but if u pass real to in 1.0 gotta be 1.0L or shit. really dat iz happening already I guess but therez conversion shit goin' down. hey mr. so i think u should check a thread on octal literalz shit a week ago or so. like octal!"177" iz 0177 and octal!"177L" iz 0177L and shit. if needed this can be done for floating shit too. T inv(T)(T shit) { return floating_literal!(T, "1.0") / shit; } floating_literal give u da right type. prolly therez some simple string mixin n shit.
 (1) Converting a floating point literal into a double literal is usually 
 not lossless.
 0.5f, 0.5, and 0.5L are all exactly the same number, since they are 
 exactly representable.
 But 0.1 is not the same as 0.1L.
 So it's a bit odd that this silent lossless conversion is taking place.
 It does have a very strong precedent from C, however.
u mean lossy not lossless homez.
 (2) The interaction between implicit casting and template parameters is 
 quite poor. Eg, the fact that '0' is an int, not a floating point type, 
   means that something simple like:
   add(T)(T x) if (isFloatingPoint!(T))
 doesn't work properly. It is not the same as:
 add(real x)
 since it won't allow add(0).
 
 Which is pretty annoying. Why can't 0 just mean zero???
yeah. and shit 0 its an octal constant. fuck.
Mar 28 2010
parent superdan <super dan.org> writes:
superdan Wrote:

 he wanna 1.0 to adapt to T. i'm sayin' if u pass float to inv 1.0 gotta be
10.F but if u pass real to in 1.0 gotta be 1.0L or shit.
dats 1.0F not 10.F. shit. but im sure u peoples feel me.
Mar 28 2010
prev sibling next sibling parent reply so <so so.do> writes:
On Mon, 29 Mar 2010 00:46:20 +0400, Don <nospam nospam.com> wrote:

 so wrote:
 Hello Don, finally!
  It is hard to explain yourself when you don't know the people you talk  
 have numeric coding background.
 Since we know you have done much numeric and generic coding, it will be  
 enough for me if you understand what i mean/ask/want,
 and say what i am talking is ridiculous, or think that i am trolling,  
 just say so and i shut up! :)
You're definitely not trolling! I'm not 100% sure of which issue you're referring too, but I'm aware of a few, for example: (1) Converting a floating point literal into a double literal is usually not lossless. 0.5f, 0.5, and 0.5L are all exactly the same number, since they are exactly representable. But 0.1 is not the same as 0.1L. So it's a bit odd that this silent lossless conversion is taking place. It does have a very strong precedent from C, however. (2) The interaction between implicit casting and template parameters is quite poor. Eg, the fact that '0' is an int, not a floating point type, means that something simple like: add(T)(T x) if (isFloatingPoint!(T)) doesn't work properly. It is not the same as: add(real x) since it won't allow add(0). Which is pretty annoying. Why can't 0 just mean zero???
At the beginning the question in my mind was, why can't we do this the way it works in math : [Real numbers] contains [Integers] contains [Natural numbers] So, when a compiler see 0, which is the element of all above, it should classify it as : 1st degree. [floating point type, integer, natural number] When it encounters say, -3, it will be : 2nd degree. [floating point type and integer] And finally when it encounters something like 4.0, it will be : 3rd degree. [floating point type] With these in mind, the prototype : T foo(T)(T m) {}, should be able to take all three degrees of types above. We go abit further and see what it actually is : T foo(T)(T m) { T n = m * 5 - 4 * 7 / 2; // numbers belongs all the systems above, this line should work for every T T k = n / 20; // same. return n + k; // same. } "T n = m * 5 - 4 * 7 / 2;" m is here in the type of T, * operator has a generic operand 5, convert it to T, if you can't, pop error. - operator has a generic operand (4 * 7 / 2), expect it in the and keep parsing. Again, in the code above, none of the constants is a native type, as in 5 is not int, it is a generic constant which belongs all systems above. What this means? calling foo(0.0f) is actually : float foo(float m) { float n = m * 5.0f - 4.0f * 7.0f / 2.0f; float k = n / 20.0f; return n + k; } m is here float and operation * has a generic operand There wasn't a single implicit cast and the code was perfectly generic. calling foo(0) will pass the "if(isFloatingPoint!(T))" contract, since 0 belongs to all, but compiler will be unable to resolve the final type which is to be instantiated. But coming with something like this would be suicide since i already got enough treatment for the lighter syntax :P Sorry for the long post! Thanks. -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
next sibling parent so <so so.do> writes:
A few errors, sorry about that!

 - operator has a generic operand (4 * 7 / 2), expect it in the and keep  
 parsing.
which should be "expect it in the type of T and keep parsing"
 m is here float and operation * has a generic operand
and ignore this line :) Thanks! -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
prev sibling parent reply Fawzi Mohamed <fawzi gmx.ch> writes:
On 28-mar-10, at 22:34, so wrote:

 On Mon, 29 Mar 2010 00:46:20 +0400, Don <nospam nospam.com> wrote:

 so wrote:
 Hello Don, finally!
 It is hard to explain yourself when you don't know the people you  
 talk have numeric coding background.
 Since we know you have done much numeric and generic coding, it  
 will be enough for me if you understand what i mean/ask/want,
 and say what i am talking is ridiculous, or think that i am  
 trolling, just say so and i shut up! :)
You're definitely not trolling! I'm not 100% sure of which issue you're referring too, but I'm aware of a few, for example: (1) Converting a floating point literal into a double literal is usually not lossless. 0.5f, 0.5, and 0.5L are all exactly the same number, since they are exactly representable. But 0.1 is not the same as 0.1L. So it's a bit odd that this silent lossless conversion is taking place. It does have a very strong precedent from C, however. (2) The interaction between implicit casting and template parameters is quite poor. Eg, the fact that '0' is an int, not a floating point type, means that something simple like: add(T)(T x) if (isFloatingPoint!(T)) doesn't work properly. It is not the same as: add(real x) since it won't allow add(0). Which is pretty annoying. Why can't 0 just mean zero???
At the beginning the question in my mind was, why can't we do this the way it works in math : [Real numbers] contains [Integers] contains [Natural numbers] So, when a compiler see 0, which is the element of all above, it should classify it as : 1st degree. [floating point type, integer, natural number] When it encounters say, -3, it will be : 2nd degree. [floating point type and integer] And finally when it encounters something like 4.0, it will be : 3rd degree. [floating point type] With these in mind, the prototype : T foo(T)(T m) {}, should be able to take all three degrees of types above. We go abit further and see what it actually is : T foo(T)(T m) { T n = m * 5 - 4 * 7 / 2; // numbers belongs all the systems above, this line should work for every T T k = n / 20; // same. return n + k; // same. } "T n = m * 5 - 4 * 7 / 2;" m is here in the type of T, * operator has a generic operand 5, convert it to T, if you can't, pop error. - operator has a generic operand (4 * 7 / 2), expect it in the and keep parsing. Again, in the code above, none of the constants is a native type, as in 5 is not int, it is a generic constant which belongs all systems above. What this means? calling foo(0.0f) is actually : float foo(float m) { float n = m * 5.0f - 4.0f * 7.0f / 2.0f; float k = n / 20.0f; return n + k; } m is here float and operation * has a generic operand There wasn't a single implicit cast and the code was perfectly generic.
why do you see implicit cast as bad? for me the change from 5 to 5.0f that you have above *is* an implicit cast, and is exactly what you want. For 4 * 7 / 2, well that should either be calculated as integer or as float, it is unlikely that both are correct. Some languages decided to use a different symbol for integer division and float division. without that the thing is kind of ambiguous, and D chooses integer division, and only later implicit conversion to float. What I do agree with is that writing generic float literals is cumbersome (using cast(T)x). Implicit cast is what happens to stuff that can also be seen as a type X so natural number -> integers,... implicit cast should not discard precision, and should maintain the value, and is a *good* thing in general (if the rules are sensible).
 calling foo(0) will pass the "if(isFloatingPoint!(T))" contract,  
 since 0 belongs to all,
 but compiler will be unable to resolve the final type which is to be  
 instantiated.
that gets easily very annoying, even aldor, that basically did all what you wanted, and complained about unclear overload did an exception for integers, so that they would default to Int.... Still I find D approach reasonable
Mar 28 2010
parent so <so so.do> writes:
 why do you see implicit cast as bad? for me the change from 5 to 5.0f  
 that you have above *is* an implicit cast, and is exactly what you want.
 For  4 * 7 / 2, well that should either be calculated as integer or as  
 float, it is unlikely that both are correct.
 Some languages decided to use a different symbol for integer division  
 and float division.
 without that the thing is kind of ambiguous, and D chooses integer  
 division, and only later implicit conversion to float.
 What I do agree with is that writing generic float literals is  
 cumbersome (using cast(T)x).
Hello! Implicit cast is bad, for performance reasons, for clarity, and you can't call it generic. With the rule i presented "5 to 5.0f" is not an implicit cast, How? It is implicit in C derived languages just because 5 is int, it has a native type, this is language constraint. In my case 5 is not a native type, and by definition 5 to 5.0f is not an implicit cast, 5 and 5.0f is the same thing! For 4 * 7 / 2, yes you are right it was an error on my side which i corrected with next reply, also i wasn't clear enough. So, i meant to say there "expect it in the type of T", and that means since the operator before them expects it in type T, Treat them as type T, if you can't, pop warning/error, so lets say T is a floating point type, compiler can do (4 * 7 / 2) operation in the highest possible precision for the constant folding purposes and return the result, which will be again a "generic floating point type".
 Implicit cast is what happens to stuff that can also be seen as a type X  
 so natural number -> integers,... implicit cast should not discard  
 precision, and should maintain the value, and is a *good* thing in  
 general (if the rules are sensible).
Perfectly fine with me when the rules are really sensible, but isn't the point of templates, being a template rather than casting? Is this a generic code then? uint add(uint m, uint n) { return m + n; } By implicit/explicit casts i can pass all the types here.
 calling foo(0) will pass the "if(isFloatingPoint!(T))" contract, since  
 0 belongs to all,
 but compiler will be unable to resolve the final type which is to be  
 instantiated.
that gets easily very annoying, even aldor, that basically did all what you wanted, and complained about unclear overload did an exception for integers, so that they would default to Int.... Still I find D approach reasonable
If we want add(0) to just work... why it should explicitly be an "int" and not a byte or real? For me converting it to highest precision integer would be better choice, maybe it is the reason why it was "int". At the time of C there wasn't any integer longer than say int? Sorry if this sounds naive! :) Thanks. -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
prev sibling next sibling parent reply Fawzi Mohamed <fawzi gmx.ch> writes:
On 28-mar-10, at 22:46, Don wrote:

 so wrote:
 Hello Don, finally!
 It is hard to explain yourself when you don't know the people you  
 talk have numeric coding background.
 Since we know you have done much numeric and generic coding, it  
 will be enough for me if you understand what i mean/ask/want,
 and say what i am talking is ridiculous, or think that i am  
 trolling, just say so and i shut up! :)
You're definitely not trolling! I'm not 100% sure of which issue you're referring too, but I'm aware of a few, for example:
ok if you have a real message, I am sorry to have been so harsh, I was a bit hard due to other trolls going on... sorry if I did not really catch your message, but next time try to actually bring an actual example of what the compiler does wrong, it will help understand your problem.
 (1) Converting a floating point literal into a double literal is  
 usually not lossless.
 0.5f, 0.5, and 0.5L are all exactly the same number, since they are  
 exactly representable.
 But 0.1 is not the same as 0.1L.
 So it's a bit odd that this silent lossless conversion is taking  
 place.
 It does have a very strong precedent from C, however.
well the correct thing to do (I thought that was what was done possibly CTFE excluded) is to always do constant folding with the maximum precision available (maybe even more than real), and only at the end convert to the type. so that one could write cast(real)0.555_555_555_555_555_555 and that is equivalent to 0.555_555_555_555_555_555L and thus basically cast(T)xxx is the generic way to write a float literal of type T. If this is not like that, then it should indeed be changed... I still argue that using integers if possible often a better choice. The language that that solved the problem of floating points, integers & co most cleanly is probably aldor (http://www.aldor.org/) but it is a dead language.
 (2) The interaction between implicit casting and template parameters  
 is quite poor. Eg, the fact that '0' is an int, not a floating point  
 type,  means that something simple like:
 add(T)(T x) if (isFloatingPoint!(T))
 doesn't work properly. It is not the same as:
 add(real x)
 since it won't allow add(0).

 Which is pretty annoying. Why can't 0 just mean zero???
and maybe 1 the identity? the aldor solution is probably a bit too challenging but allowed both things... Integer were arbitrary length integrals converted to the final type with implict cast (fromInteger), floats were converted to Rationals and then to the correct type with implicit cast (fromRational). 0 and 1 could be implicitly casted separately to allow things like vectors and matrixes to use them without defining a full fromInteger cast. I find that for integers D normally does the correct thing, and for floats I thought it was closer that what it might be (i.e. arbitrary precision calculations and cast to the final type). about the isFloatingPoint one can still one can easily write add(T)(T x) if(is(T:real)) or something like that, and probably solve the problem. what would be nice is to have templates to find types with a given precision and so like one does in fortran. also i use templates to get the complex type or real type of a given type and so on. These things should probably be in the library... so with some extra templates probably these things could be solved.
Mar 28 2010
parent so <so so.do> writes:
On Mon, 29 Mar 2010 01:45:09 +0400, Fawzi Mohamed <fawzi gmx.ch> wrote:

 ok if you have a real message, I am sorry to have been so harsh, I was a  
 bit hard due to other trolls going on...
 sorry if I did not really catch your message, but next time try to  
 actually bring an actual example of what the compiler does wrong, it  
 will help understand your problem.
D is the best language i have seen, and that is why i am here. The point i am trying to make is something you all mostly say in this newsgroup. Even it is the language choice of mine, C++ made some major mistakes by blindly copying some of the rules/syntax C has, and tried to build many things on top of them. As Walter said for the arrays not being first class citizens in C and unfortunately C++. For me this is a case exactly like arrays... For a mainly generic/system language, 0 shouldn't be an int and 0.0 should not be a double, and i am having hard time to find reasons why it is still as it is other than backwards/root compatibility. Thanks.
 (1) Converting a floating point literal into a double literal is  
 usually not lossless.
 0.5f, 0.5, and 0.5L are all exactly the same number, since they are  
 exactly representable.
 But 0.1 is not the same as 0.1L.
 So it's a bit odd that this silent lossless conversion is taking place.
 It does have a very strong precedent from C, however.
well the correct thing to do (I thought that was what was done possibly CTFE excluded) is to always do constant folding with the maximum precision available (maybe even more than real), and only at the end convert to the type. so that one could write cast(real)0.555_555_555_555_555_555 and that is equivalent to 0.555_555_555_555_555_555L and thus basically cast(T)xxx is the generic way to write a float literal of type T. If this is not like that, then it should indeed be changed... I still argue that using integers if possible often a better choice. The language that that solved the problem of floating points, integers & co most cleanly is probably aldor (http://www.aldor.org/) but it is a dead language.
 (2) The interaction between implicit casting and template parameters is  
 quite poor. Eg, the fact that '0' is an int, not a floating point  
 type,  means that something simple like:
 add(T)(T x) if (isFloatingPoint!(T))
 doesn't work properly. It is not the same as:
 add(real x)
 since it won't allow add(0).

 Which is pretty annoying. Why can't 0 just mean zero???
and maybe 1 the identity? the aldor solution is probably a bit too challenging but allowed both things... Integer were arbitrary length integrals converted to the final type with implict cast (fromInteger), floats were converted to Rationals and then to the correct type with implicit cast (fromRational). 0 and 1 could be implicitly casted separately to allow things like vectors and matrixes to use them without defining a full fromInteger cast. I find that for integers D normally does the correct thing, and for floats I thought it was closer that what it might be (i.e. arbitrary precision calculations and cast to the final type). about the isFloatingPoint one can still one can easily write add(T)(T x) if(is(T:real)) or something like that, and probably solve the problem. what would be nice is to have templates to find types with a given precision and so like one does in fortran. also i use templates to get the complex type or real type of a given type and so on. These things should probably be in the library... so with some extra templates probably these things could be solved.
-- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Don wrote:
 (1) Converting a floating point literal into a double literal is usually 
 not lossless.
 0.5f, 0.5, and 0.5L are all exactly the same number, since they are 
 exactly representable.
 But 0.1 is not the same as 0.1L.
It depends. The D compiler internally stores all floating point constants, regardless of type, in full 80 bit precision. Constant folding and CTFE are all carried out in 80 bit precision regardless of type. The only time it is actually truncated to the shorter formats is when writing it out to the object file. The central idea is that more precision == better. If your floating point algorithm breaks if precision increases, then the algorithm is a bad one. The only time I've seen code that relied on roundoff errors is in test suites that specifically tested for it.
Mar 28 2010
next sibling parent Fawzi Mohamed <fawzi gmx.ch> writes:
On 29-mar-10, at 00:04, Walter Bright wrote:

 Don wrote:
 (1) Converting a floating point literal into a double literal is  
 usually not lossless.
 0.5f, 0.5, and 0.5L are all exactly the same number, since they are  
 exactly representable.
 But 0.1 is not the same as 0.1L.
It depends. The D compiler internally stores all floating point constants, regardless of type, in full 80 bit precision. Constant folding and CTFE are all carried out in 80 bit precision regardless of type. The only time it is actually truncated to the shorter formats is when writing it out to the object file.
ok good that is actually how I though, so that cast(T)x is indeed the way to write a generic float literal. I thought that was the case, but seeing Don implying something different I got worried that my coding was based on wrong assumptions.
 The central idea is that more precision == better. If your floating  
 point algorithm breaks if precision increases, then the algorithm is  
 a bad one.
I fully agree.
 The only time I've seen code that relied on roundoff errors is in  
 test suites that specifically tested for it.
Mar 28 2010
prev sibling parent reply Don <nospam nospam.com> writes:
Walter Bright wrote:
 Don wrote:
 (1) Converting a floating point literal into a double literal is 
 usually not lossless.
 0.5f, 0.5, and 0.5L are all exactly the same number, since they are 
 exactly representable.
 But 0.1 is not the same as 0.1L.
It depends. The D compiler internally stores all floating point constants, regardless of type, in full 80 bit precision. Constant folding and CTFE are all carried out in 80 bit precision regardless of type. The only time it is actually truncated to the shorter formats is when writing it out to the object file. The central idea is that more precision == better. If your floating point algorithm breaks if precision increases, then the algorithm is a bad one. The only time I've seen code that relied on roundoff errors is in test suites that specifically tested for it.
There's some oddities. //enum A = 1.0e400; // Error: number is not representable enum B = 1.0e200 * 1e200; // yes it is! enum C = 1.0e400L; static assert(B==C); So there's a bit of schizophrenia about whether the 'L' suffix changes which values are representable, or whether it is just a type marker. I think we should tighten things up a little bit, but I don't think it's a big deal.
Mar 29 2010
parent Fawzi Mohamed <fawzi gmx.ch> writes:
On 29-mar-10, at 10:29, Don wrote:

 Walter Bright wrote:
 Don wrote:
 (1) Converting a floating point literal into a double literal is  
 usually not lossless.
 0.5f, 0.5, and 0.5L are all exactly the same number, since they  
 are exactly representable.
 But 0.1 is not the same as 0.1L.
It depends. The D compiler internally stores all floating point constants, regardless of type, in full 80 bit precision. Constant folding and CTFE are all carried out in 80 bit precision regardless of type. The only time it is actually truncated to the shorter formats is when writing it out to the object file. The central idea is that more precision == better. If your floating point algorithm breaks if precision increases, then the algorithm is a bad one. The only time I've seen code that relied on roundoff errors is in test suites that specifically tested for it.
There's some oddities. //enum A = 1.0e400; // Error: number is not representable enum B = 1.0e200 * 1e200; // yes it is! enum C = 1.0e400L; static assert(B==C); So there's a bit of schizophrenia about whether the 'L' suffix changes which values are representable, or whether it is just a type marker. I think we should tighten things up a little bit, but I don't think it's a big deal.
good to have the numerics expert look into this. Yes I think the situation is really quite ok, for example (now having some doubts) I just checked (to be sure the correct thing would happens as I thought) and T mult1(T)(T x){ return 1.000000000000000001*x; } assert(mult1(1.0f)==1.0f,"float"); assert(mult1(1.0)==1.0,"double"); assert(mult1(1.0L)==1.0L*1.000000000000000001L,"real"); :)
Mar 29 2010