www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Operator overloading, structs

reply bearophile <bearophileHUGS lycos.com> writes:
This post is mostly about D2 operator overloading in general, but I also talk
about problems in the API of Tango BigInts.

A small program that use multiprecision integers of Tango:


import tango.stdc.stdio: printf;
import tango.math.BigInt: bint = BigInt;

void main() {
    bint i = 1; // #1
    if (i) // #2
        i++;
    auto a = [10, 20, 30, 40];
    printf("%d\n", a[i]); // #3
}


If you replace BigInt with int that program works.
With bigInteger it doesn't work, because I think:
- In #1 it doesn't call static opCall.
- In #2 D doesn't have a standard method that returns true/false. In Python2.6
such method is named __nonzero__ and in python3 it's named __bool__.
- In #3 there's no implicit cast to int.

I think that improving such D2 operator overloading is very good, it allows to
use BigInts, SafeInts, Complex, etc, in a quite more transparent way, allowing
very similar code to work with native and user defined types.

---------------

Regarding specifically BigInt:

D1 has opCast, but here I think not even a[cast(long)i] works, because BigInt
doesn't define it yet. When a big int can't be converted to long, it can throw
an exception.

Until a more efficient solution is found, I think BigInts must have a toString
too. In most situations you don't print such numbers, you do lot of
computations with them and then you finally print some of them. So often the
time spent printing them is not much).

I think BigInt also may enjoy a lot methods like: int opEquals(int y), etc.

The following code works, but I can't use code like this:


import tango.stdc.stdio: printf;
import tango.math.BigInt: bint = BigInt;
import tango.io.Console: Cout;
import Integer = tango.text.convert.Integer;

void main() {
    bint i = bint(1);
    if (i != bint(0))
        i++;
    auto si = i.toDecimalString();
    Cout(si).newline;
    auto a = [10, 20, 30, 40];
    printf("%d\n", a[Integer.parse(si)]);
}

(Maybe there are ways to improve that code a bit already.)

Bye,
bearophile
May 28 2009
parent reply Don <nospam nospam.com> writes:
bearophile wrote:
 This post is mostly about D2 operator overloading in general, but I also talk
about problems in the API of Tango BigInts.
 
 A small program that use multiprecision integers of Tango:
 
 
 import tango.stdc.stdio: printf;
 import tango.math.BigInt: bint = BigInt;
 
 void main() {
     bint i = 1; // #1
     if (i) // #2
         i++;
     auto a = [10, 20, 30, 40];
     printf("%d\n", a[i]); // #3
 }
 
 
 If you replace BigInt with int that program works.
 With bigInteger it doesn't work, because I think:
 - In #1 it doesn't call static opCall.

as well.
 - In #2 D doesn't have a standard method that returns true/false. In Python2.6
such method is named __nonzero__ and in python3 it's named __bool__.
 - In #3 there's no implicit cast to int.

 
 I think that improving such D2 operator overloading is very good, it allows to
use BigInts, SafeInts, Complex, etc, in a quite more transparent way, allowing
very similar code to work with native and user defined types.
 
 ---------------
 
 Regarding specifically BigInt:
 
 D1 has opCast, but here I think not even a[cast(long)i] works, because BigInt
doesn't define it yet. When a big int can't be converted to long, it can throw
an exception.

Yes, that's not a bad idea.
 Until a more efficient solution is found, I think BigInts must have a toString
too. In most situations you don't print such numbers, you do lot of
computations with them and then you finally print some of them. So often the
time spent printing them is not much).

Not toString(), though. You MUST be able to specify if you want leading zeros, and if you want hex or decimal. I just (a) haven't got around to it; and (b) haven't been sure about the interface should be. But I hate toString() so much, I'm _never_ going to support it. It's an OMDB (over my dead body). Sorry.
 I think BigInt also may enjoy a lot methods like: int opEquals(int y), etc.

Fixed in Tango SVN 4717.
Jun 01 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
 - In #2 D doesn't have a standard method that returns true/false. In Python2.6
such method is named __nonzero__ and in python3 it's named __bool__.<<


No one has commented about that, but I think that having a way to overload cast(bool)foo is important. I use it in Python and I have shown why and how it can be used in D too. It's an easy thing to do and I think it's safe. Don:
 D1 has opCast, but here I think not even a[cast(long)i] works, because BigInt
doesn't define it yet. When a big int can't be converted to long, it can throw
an exception.

Yes, that's not a bad idea.

But eventually an implicit cast will be better (even if a bit less safe) in D2.
 Not toString(), though. You MUST be able to specify if you want leading 
 zeros, and if you want hex or decimal.

toString() is for the default case, when you just want the decimal number with no leading zeros. Then you can add other methods to output hex and all you want. (In what situations do you want to print leading zeros of a big multiprecision integral value? I have never faced such need so far).
 But I hate toString() so much, I'm _never_ going to support it. It's an 
 OMDB (over my dead body). Sorry.

I have patched a copy of the bigint module to add the toString method. I'll keep using such patch for my programs. I want you well alive and happy too.
 Fixed in Tango SVN 4717.

Thank you. Bye, bearophile
Jun 01 2009
parent reply Don <nospam nospam.com> writes:
bearophile wrote:
 - In #2 D doesn't have a standard method that returns true/false. In Python2.6
such method is named __nonzero__ and in python3 it's named __bool__.<<


No one has commented about that, but I think that having a way to overload cast(bool)foo is important. I use it in Python and I have shown why and how it can be used in D too. It's an easy thing to do and I think it's safe.

It's definitely required for completeness. In C++ there's a hack to do it safely (you return a pointer to a private member class). Still, I wonder if D could simply do something like defining that for classes: if (x) is always transformed into if ( x!=0 ) if (!x) is always transformed into if ( x==0 )
 
 Don:
 
 D1 has opCast, but here I think not even a[cast(long)i] works, because BigInt
doesn't define it yet. When a big int can't be converted to long, it can throw
an exception.


But eventually an implicit cast will be better (even if a bit less safe) in D2.

On second thoughts, y = x.toLong or y = to!(long)(x) is probably better. Casts are evil, implicit casts even more so.
 
 Not toString(), though. You MUST be able to specify if you want leading 
 zeros, and if you want hex or decimal.

toString() is for the default case, when you just want the decimal number with no leading zeros. Then you can add other methods to output hex and all you want. (In what situations do you want to print leading zeros of a big multiprecision integral value? I have never faced such need so far).

BigFloat, for example.
Jun 01 2009
next sibling parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Don wrote:
 ...
 
 On second thoughts, y = x.toLong or y = to!(long)(x) is probably better.
 Casts are evil, implicit casts even more so.

If you add a toLong or to_long member to BigInt, then to!(long)(x) will work automatically. Something similar applies to fromLong/from_long and to!(BigInt)(cast(long)y).
Jun 02 2009
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Don:

 Still, I wonder if D could simply do something like defining that for classes:
 if (x)  is always transformed into  if ( x!=0 )
 if (!x) is always transformed into  if ( x==0 )

Explicit is better than implicit. For example I'd like to have a set collection that is "false" when empty. In such situation I can write just the following method, it seems simpler and semantically cleaner than alternatives: bool opBool() { return this.length != 0; }
On second thoughts, y = x.toLong or y = to!(long)(x) is probably better. Casts
are evil, implicit casts even more so.<

I don't know. Using esplicit casts/methods may be safer. So with your current changes to BigInt, in the following program to replace the int "i" with a BigInt: void main() { int i = 1; if (i) i++; auto a = [10, 20, 30, 40]; printf("%d\n", a[i]); } you need to change the code like this: void main() { BigInt i = 1; if (i != 0) i++; auto a = [10, 20, 30, 40]; printf("%d\n", to!(long)a[i]); } The toBool will help avoid the change in the second line. I'd like to change programs as little as possible when I change the type of a variable from int to BigInt. This has also the big advantage that I can write templated algorithms that work with both ints and BigInts with as few "static if" as possible (to manage BigInts in a special way, for example adding that to!(long) ). That's why in such situation an implicit casting is handy. ------------- Such almost-transparent replacement of ints with BigInts can be done also if BigInts are fast to perform operations with small integers (like with integers in range -1073741824 .. 1073741824). I have done few easy benchmarks (that I can show you if you want) and I have seen that when a bigint contains only ~30 bits of data or less a BigInt is much slower than an int. Some speed difference is inevitable, but to help such replacement I think it can be good if BigInts gain speed optimizations for small such numbers. (And when you use BigInts that contain 5000+ bits such optimizazions don't slow down BigInts significantly). Possible idea: you can test if the number needs less than 31 bits, if so, you can compute the operation using just a "long". If the result then can be stored back in about 31 bits, then you are done. This is slower than a simple operation among "ints" but it may be much faster than the same operations done with BigInts. When numbers are so small the BigInt may also avoid all heap activity (Lisp languages do this using tags, they use a tagged pointer, that can be a small integer, avoiding any memory allocation when the number is small enough). Bye, bearophile
Jun 02 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
bearophile wrote:

 

 So with your current changes to BigInt, in the following program to

 
 void main() {
     int i = 1;
     if (i)
         i++;
     auto a = [10, 20, 30, 40];
     printf("%d\n", a[i]);
 }
 
 you need to change the code like this:
 
 void main() {
     BigInt i = 1;
     if (i != 0)
         i++;
     auto a = [10, 20, 30, 40];
     printf("%d\n", to!(long)a[i]);
 }
 
 The toBool will help avoid the change in the second line.
 
 I'd like to change programs as little as possible when I change the
 type of a variable from int to BigInt. This has also the big
 advantage that I can write templated algorithms that work with both
 ints and BigInts with as few "static if" as possible (to manage
 BigInts in a special way, for example adding that to!(long) ). That's
 why in such situation an implicit casting is handy.
 

why not just use (i != 0) in both cases? this should work with any numeric type (so it'll be used in generic code). conversion of ints to bools in C is IMO a hole in the type system due to the lack of a boolean type in C. All those narrowing implicit casts inherited from C are a bad idea IMO. the need to convert the bigInt to long in order to print it is a design error in printf() - the format string should specify the formatting of the variables not their types.
Jun 02 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Yigal Chripun:

 why not just use (i != 0) in both cases?

Yes, here it can be done (if Don has implemented opEquals(int y)), but you don't need that once you have something like opBool.
 conversion of ints to bools in C is IMO a hole in the type system due to 
 the lack of a boolean type in C. All those narrowing implicit casts 
 inherited from C are a bad idea IMO.

Walter was right, converting a generic integral to bool requires some time, that in the middle of the the nested loop can slow down code a bit (5%, in a case of mine). A standard opBool method allows you to tell when a collection is empty, like an empty string, an empty range, an empty array, and so on. you can use it instead of somecollection.isEmpty(). Java as Pascal keep booleans and integers separated, this looks tidy and clean. I love clean things, but in practice I don't think the current design of D (and C, Python, and several other languages) leads to a significant amount of bugs. It seems one of those situations where practical considerations win over purity. There are other situations where I'd like to see fixed some implicit type conversions. C# shows a saner design in such regard. There are few things in C# that I'd like to see copied by D.
 the need to convert the bigInt to long in order to print it is a design 
 error in printf() - the format string should specify the formatting of 
 the variables not their types.

printf was invented lot of time ago, in galaxy far away, when needs and situations were different. That code of mine is wrong, this is more correct, so here it's not printf fault: printf("%d\n", a[to!(long)i]); Because it's "i" the BigInt, not the contents of the "a" array. Bye, bearophile
Jun 02 2009
next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from bearophile (bearophileHUGS lycos.com)'s article
 Yigal Chripun:
 why not just use (i != 0) in both cases?


 conversion of ints to bools in C is IMO a hole in the type system due to
 the lack of a boolean type in C. All those narrowing implicit casts
 inherited from C are a bad idea IMO.


 A standard opBool method allows you to tell when a collection is empty, like an

somecollection.isEmpty().
 Java as Pascal keep booleans and integers separated, this looks tidy and clean.

Python, and several other languages) leads to a significant amount of bugs. It seems one of those situations where practical considerations win over purity. For D and other close to the metal languages, IMHO the int-bool relationship should stay the way it is simply because that's the way it works on the bare metal (at least on x86, for example, instructions like jz). I think this is a good rule of thumb in a close to the metal language, and even in the general case: When in doubt, do the thing that gives the user the least obstructed view of how things "really" work at the next lower level. This avoids lots of silly abstraction inversions (One of which is building a strong boolean type on top of an int).
Jun 02 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
dsimcha wrote:
 == Quote from bearophile (bearophileHUGS lycos.com)'s article
 Yigal Chripun:
 why not just use (i != 0) in both cases?

but you don't

 conversion of ints to bools in C is IMO a hole in the type system
  due to the lack of a boolean type in C. All those narrowing 
 implicit casts inherited from C are a bad idea IMO.

some time, that

a case of mine).
 A standard opBool method allows you to tell when a collection is 
 empty, like an

it instead of somecollection.isEmpty().
 Java as Pascal keep booleans and integers separated, this looks 
 tidy and clean.

of D (and C, Python, and several other languages) leads to a significant amount of bugs. It seems one of those situations where practical considerations win over purity. For D and other close to the metal languages, IMHO the int-bool relationship should stay the way it is simply because that's the way it works on the bare metal (at least on x86, for example, instructions like jz). I think this is a good rule of thumb in a close to the metal language, and even in the general case: When in doubt, do the thing that gives the user the least obstructed view of how things "really" work at the next lower level. This avoids lots of silly abstraction inversions (One of which is building a strong boolean type on top of an int).

close to the metal does not imply in any way you code with binary op-codes or assembly concepts. all it means is that it does not force upon you costly abstractions and mechanisms like a VM. this does not apply to this discussion since a bool type doesn't require a VM and does not penalize performance in any way. your abstraction inversion example doesn't apply here. The problem I see is the narrowing implicit cast, i.e. int values behave like booleans. I have no problem with the reverse which is what your example is about.
Jun 03 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Yigal Chripun wrote:
 your abstraction inversion example doesn't apply here. The problem I see 
 is the narrowing implicit cast, i.e. int values behave like booleans. I 
 have no problem with the reverse which is what your example is about.

An int does not convert to bool implicitly. An int can be tested with "if", which is a different thing. Andrei
Jun 03 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 your abstraction inversion example doesn't apply here. The problem I 
 see is the narrowing implicit cast, i.e. int values behave like 
 booleans. I have no problem with the reverse which is what your 
 example is about.

An int does not convert to bool implicitly. An int can be tested with "if", which is a different thing. Andrei

that is an implicit cast. what I'm saying is that: int a = .. ; if (a) { .. } this should be a compiler error IMO.
Jun 04 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Yigal Chripun:
 that is an implicit cast.
 what I'm saying is that:
 
   int a = .. ;
   if (a) { .. }
 
 this should be a compiler error IMO.

I think the opposite is good: that empty collections are "false", so the following ones print X: int[] a; if (!a) printf("X"); Set!(int) s; if (!s) printf("X"); int[string] aa; if (!aa) printf("X"); Bye, bearophile
Jun 04 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
bearophile wrote:
 Yigal Chripun:
 that is an implicit cast.
 what I'm saying is that:

   int a = .. ;
   if (a) { .. }

 this should be a compiler error IMO.

I think the opposite is good: that empty collections are "false", so the following ones print X: int[] a; if (!a) printf("X"); Set!(int) s; if (!s) printf("X"); int[string] aa; if (!aa) printf("X"); Bye, bearophile

what's so bad about using a general collection.empty() API?
 int[]  a;
 if (!a.empty) printf("X");
 Set!(int) s;
 if (!s.empty) printf("X");
 int[string] aa;
 if (!aa.empty) printf("X");

what's more readable, my version or yours?
Jun 04 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Yigal Chripun:

 what's more readable, my version or yours?

Mine, ihmo :-) Less things to remember. And the code is shorter and less noisy. Java programmers seems to ignore how much noisy is their code. "empty collections are false" is easy. You don't have to write code like: string s; if (s == null) ... if (s == "") ... if (s.length == 0) ... if (s.length) ... Or even: if (s is null) ... And few other variants I have seen in D code or snippets. Such things Do confuse d newbies (see digitalmars.D.learn). (If a collection is a class, and the object doesn't exists yet, and the variable is just a null reference, then it's false anywaythe collection is empty still. Do you like this?).
is a bad pattern.<

Why?
it is even more problematic with floats.<

I think I have never had troubles from zero testing of floats/double/reals in D. If you want to try to convince other people (and I don't think D will change on this) you have to start listing some downsides of the current design, some real bugs it leads to, and so on. Bye, bearophile
Jun 04 2009
parent Yigal Chripun <yigal100 gmail.com> writes:
bearophile wrote:
 Yigal Chripun:
 
 what's more readable, my version or yours?

Mine, ihmo :-) Less things to remember. And the code is shorter and less noisy. Java programmers seems to

 "empty collections are false" is easy. You don't have to write code
 like: > string s;
 if (s == null) ...
 if (s == "") ...
 if (s.length == 0) ...
 if (s.length) ...
 Or even:
 if (s is null) ...
 And few other variants I have seen in D code or snippets. Such things
 

 (If a collection is a class, and the object doesn't exists yet, and

collection is empty still. Do you like this?).
 
 
 is a bad pattern.<

Why?
 it is even more problematic with floats.<

I think I have never had troubles from zero testing of

 
 If you want to try to convince other people (and I don't think D will
 

design, some real bugs it leads to, and so on.
 
 Bye,
 bearophile

for starters, null and empty are two distinct concepts and sometimes it is important to know the difference.
 if (s is null) ...
 if (s == "") ... // NOT same thing as above

also, with your version you need to remember _more_ things when reading code. I agree that the code is shorter but this is insignificant in this case. Java code has nothing to do with this and this is NOT the reason why Java code can be noisy.
Jun 05 2009
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Thu, 04 Jun 2009 10:06:45 +0300, Yigal Chripun wrote:

 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 your abstraction inversion example doesn't apply here. The problem I 
 see is the narrowing implicit cast, i.e. int values behave like 
 booleans. I have no problem with the reverse which is what your 
 example is about.

An int does not convert to bool implicitly. An int can be tested with "if", which is a different thing. Andrei

that is an implicit cast. what I'm saying is that: int a = .. ; if (a) { .. } this should be a compiler error IMO.

I'm not agreeing with you, Yigal. I think that the idiom you described is not equivalent to if (a == TRUE) { .. } but really if (a != 0) { .. } when 'a' is an integer of any size or sign. This should *not* be a compiler error as it is a convenient shorthand for some coders. Personally, I try not to code this idiom because I find it misleading in terms of self documentation ... but then I'm against using goto as well ;-) -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Jun 04 2009
parent Yigal Chripun <yigal100 gmail.com> writes:
Derek Parnell wrote:
 On Thu, 04 Jun 2009 10:06:45 +0300, Yigal Chripun wrote:
 
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 your abstraction inversion example doesn't apply here. The problem I 
 see is the narrowing implicit cast, i.e. int values behave like 
 booleans. I have no problem with the reverse which is what your 
 example is about.

"if", which is a different thing. Andrei

what I'm saying is that: int a = .. ; if (a) { .. } this should be a compiler error IMO.

I'm not agreeing with you, Yigal. I think that the idiom you described is not equivalent to if (a == TRUE) { .. } but really if (a != 0) { .. } when 'a' is an integer of any size or sign. This should *not* be a compiler error as it is a convenient shorthand for some coders. Personally, I try not to code this idiom because I find it misleading in terms of self documentation ... but then I'm against using goto as well ;-)

I don't see how we disagree since you say yourself that you try to avoid this idiom and find it misleading in terms of documentation. which I agree with. yes, it does save a few key strokes but as I said before, that's a really bad optimization since code is read much more often than it's written and readability is much more important than saving a few key strokes. I also agree about goto. there are very rare cases where it is useful but outside those goto is a bad bad thing to use.
Jun 04 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 your abstraction inversion example doesn't apply here. The problem I 
 see is the narrowing implicit cast, i.e. int values behave like 
 booleans. I have no problem with the reverse which is what your 
 example is about.

An int does not convert to bool implicitly. An int can be tested with "if", which is a different thing. Andrei

that is an implicit cast.

No. An implicit cast is this: int a; bool b = a; // doesn't compile or this: void fun(bool); fun(5); // doesn't compile You are mistakenly presupposing that if() takes a bool. In reality if() accepts a bool, an integral, a floating-point type, a pointer, an array, or a class reference. Andrei
Jun 04 2009
next sibling parent reply Max Samukha <outer space.com> writes:
On Thu, 04 Jun 2009 10:41:31 -0500, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:


You are mistakenly presupposing that if() takes a bool. In reality if() 
accepts a bool, an integral, a floating-point type, a pointer, an array, 
or a class reference.

or delegate
Jun 04 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Max Samukha wrote:
 On Thu, 04 Jun 2009 10:41:31 -0500, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 
 
 You are mistakenly presupposing that if() takes a bool. In reality if() 
 accepts a bool, an integral, a floating-point type, a pointer, an array, 
 or a class reference.

or delegate

I was sure I forgot something... and hash too. Anything that can be compared against 0 or null. Andrei
Jun 04 2009
next sibling parent Max Samukha <outer space.com> writes:
On Thu, 04 Jun 2009 13:10:58 -0500, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:

Max Samukha wrote:
 On Thu, 04 Jun 2009 10:41:31 -0500, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 
 
 You are mistakenly presupposing that if() takes a bool. In reality if() 
 accepts a bool, an integral, a floating-point type, a pointer, an array, 
 or a class reference.

or delegate

I was sure I forgot something... and hash too. Anything that can be compared against 0 or null. Andrei

IfStatement::semantic checks Expression::checkToBoolean, which for delete, assignment etc issues an error and for other expressions calls Type::checkBoolean, which for most types except arrays, class references etc. calls isscalar().
Jun 05 2009
prev sibling parent reply Don <nospam nospam.com> writes:
Denis Koroskin wrote:
 On Thu, 04 Jun 2009 22:10:58 +0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Max Samukha wrote:
 On Thu, 04 Jun 2009 10:41:31 -0500, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 You are mistakenly presupposing that if() takes a bool. In reality 
 if() accepts a bool, an integral, a floating-point type, a pointer, 
 an array, or a class reference.


I was sure I forgot something... and hash too. Anything that can be compared against 0 or null. Andrei

Is it considered a good practice? Technically, the following construct is not exactly portable: float f = ..; if (f) { } because C (nor D) standard doesn't guaranty that float(0) will be implemented as "all bits set to 0" on target platform (although it currently holds true).

That _is_ guaranteed. Even on not-quite-conformant systems. Actually, -0.0 is not implemented as "all-bits-zero", yet this works: void main(){ float f = -0.0; if (f) assert(0); } There's no portability problem here. To fix the original issue, we'd just need to allow something like: struct S { bool opEquals(int x) { return (x!=0); } } void main() { S x; if (x) assert(0); } That is, if x is a struct, convert "if(x)" into "if(x==0)" and convert "if(!x)" into "if(x!=0)". Usual rules would then apply, so any opEquals with a parameter which could be implicitly cast from 0 would work. That wouldn't allow smart-pointer structs to implement if (x) without requiring them to allow comparisons with integers, so it might be necessary to add a second step: try "if(x==null)" instead. Alternatively, introduce a "bool opNull()" member function for structs. Both solutions are easy to implement.
Jun 08 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Don:
 That is, if x is a struct, convert "if(x)" into "if(x==0)"
 and convert "if(!x)" into "if(x!=0)". Usual rules would then apply, so 
 any opEquals with a parameter which could be implicitly cast from 0 
 would work.
 That wouldn't allow smart-pointer structs to implement if (x) without 
 requiring them to allow comparisons with integers, so it might be 
 necessary to add a second step: try "if(x==null)" instead.

I don't like this solution, because it relies on a bit of invisible magic, and you too have seen it needs extensions because it's not general. If you want to use if (x) where x doesn't represent a number but something else like a collection, they you have to add even more special cases like add a third step: "if(x.length == 0)" etc. So this is not a good solution.
 Alternatively, introduce a "bool opNull()" member function for structs.

A similar method (we can discuss its name, that must be related to its semantic(*)) requires no invisible magic, it's clean and easy to use and implement, and it works on all three cases we have shown. This is a better solution. (*) I have suggested a name like bool opBool(), so if you perform a cast(bool)x this is the method that is called. But someone has said that in the case of if(x) that's not the method that is implicitly called... But it may be better to call it instead. So for me it's better to leave this to someone more expert than me. Bye, bearophile
Jun 09 2009
next sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
bearophile, el  9 de junio a las 08:19 me escribiste:
 Don:
 That is, if x is a struct, convert "if(x)" into "if(x==0)"
 and convert "if(!x)" into "if(x!=0)". Usual rules would then apply, so 
 any opEquals with a parameter which could be implicitly cast from 0 
 would work.
 That wouldn't allow smart-pointer structs to implement if (x) without 
 requiring them to allow comparisons with integers, so it might be 
 necessary to add a second step: try "if(x==null)" instead.

I don't like this solution, because it relies on a bit of invisible magic, and you too have seen it needs extensions because it's not general. If you want to use if (x) where x doesn't represent a number but something else like a collection, they you have to add even more special cases like add a third step: "if(x.length == 0)" etc. So this is not a good solution.

I think the more general solution is to allow multiple implicit cast operators and simply provide implicit conversion to bool for the classes you want to be able to do if (x) on. -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ----------------------------------------------------------------------------
Jun 09 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Leandro Lucarella:
 I think the more general solution is to allow multiple implicit cast
 operators and simply provide implicit conversion to bool for the classes
 you want to be able to do if (x) on.

Yes, that's a more general solution, but I think Don thinks that solution is also less safe. I don't have enough experience on this to be able to tell, but generally the less invisible magic there is, the more clear the programs are. So better to limit the magic the few places where it's very useful (and where people know very well it can be present). Bye, bearophile
Jun 09 2009
parent reply Don <nospam nospam.com> writes:
bearophile wrote:
 Leandro Lucarella:
 I think the more general solution is to allow multiple implicit cast
 operators and simply provide implicit conversion to bool for the classes
 you want to be able to do if (x) on.


No, it's not more general. You do NOT want to allow conversion to bool. The reason is that bool can itself be implicitly converted, eg this compiles: bool b = true; int y = b; -- And that's a disaster: struct Foo { bool opImplicitCast() { return true; } } Foo x; if (x) { ... } // OK int y = x; // This would compile!!!! C++ libraries go to a fair bit of trouble to allow if(x) without allowing x to be converted to bool.
Jun 09 2009
parent reply Leandro Lucarella <llucax gmail.com> writes:
Don, el 10 de junio a las 02:02 me escribiste:
 bearophile wrote:
Leandro Lucarella:
I think the more general solution is to allow multiple implicit cast
operators and simply provide implicit conversion to bool for the classes
you want to be able to do if (x) on.


No, it's not more general.

*it is* more general, that's exactly what you don't like about it =)
 You do NOT want to allow conversion to bool.  The reason is that bool
 can itself be implicitly converted, eg this compiles:
 
    bool b = true;
    int y = b;
 
 --
 And that's a disaster:
 
 struct Foo
 {
   bool opImplicitCast() { return true; }
 }
 
 Foo x;
 if (x) { ... }  // OK
 int y = x;      // This would compile!!!!
 
 C++ libraries go to a fair bit of trouble to allow if(x) without
 allowing x to be converted to bool.

I don't think is a *disaster*, but I agree that maybe the distintion can be useful (I didn't though about that). That makes me think, why not to disable implicit cast from bool to int? It's even defined what the value of int x = true; should be? I guess there are plenty of cases where you don't want that, but I can't think of anything now... Of course, int to bool implicit cast should still be possible. -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Le pedí que me enseñe a usar el mouse Pero solo quiere hablarme del Bauhaus Le pregunté si era chorra o rockera Me dijo "Gertrude Stein era re-tortillera" Me hizo mucho mal la cumbiera intelectual
Jun 09 2009
parent reply Don <nospam nospam.com> writes:
Leandro Lucarella wrote:
 Don, el 10 de junio a las 02:02 me escribiste:
 bearophile wrote:
 Leandro Lucarella:
 I think the more general solution is to allow multiple implicit cast
 operators and simply provide implicit conversion to bool for the classes
 you want to be able to do if (x) on.



*it is* more general, that's exactly what you don't like about it =)

If you consider "also introduces bugs" as "more general"... I just call that "wrong" <g>.
 
 You do NOT want to allow conversion to bool.  The reason is that bool
 can itself be implicitly converted, eg this compiles:

    bool b = true;
    int y = b;

 --
 And that's a disaster:

 struct Foo
 {
   bool opImplicitCast() { return true; }
 }

 Foo x;
 if (x) { ... }  // OK
 int y = x;      // This would compile!!!!

 C++ libraries go to a fair bit of trouble to allow if(x) without
 allowing x to be converted to bool.

I don't think is a *disaster*, but I agree that maybe the distintion can be useful (I didn't though about that).

It's very important. If you allow implicit conversion to bool, all kinds of garbage will compile. You might as well abandon static typing. That makes me think, why not to
 disable implicit cast from bool to int? It's even defined what the value
 of int x = true; should be?

 I guess there are plenty of cases where you
 don't want that, but I can't think of anything now...

I can't think of any case where it's a good idea. (It's required in D1 because opEquals() stupidly returns int). But that still leaves the problem: bool b = x; // doesn't compile if x is a class, or an array, or an AA. But it would compile if x is a struct with implicit conversion to bool.
 Of course, int to bool implicit cast should still be possible.

No. It isn't legal now! Only the special case of literals 0 and 1. Which is perfect. The only time when you want to allow an implicit conversion to bool is when you have some kind of "smart bool" struct which is supposed to be a drop-in replacement for "bool". It's quite distinct from if(x) --> if(x!=0). The strength of my proposal is that it would allow existing code to "just work" without modification. You wouldn't have to think about it. To argue against it, find a situation where it would be a problem.
Jun 09 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Don:
 It's very important. If you allow implicit conversion to bool, all kinds 
 of garbage will compile. You might as well abandon static typing.

Some half-backed ideas. opBool() can be called implicitly when it's required a truth value test, like in if(x), while(x || y), etc. So this produces a type error: bool b = x; and you need the following to call opBool: bool b = cast(bool)x; An alternative solution that I think covers most possible usages of opBool is to try replace: if (x) with if(x.length != 0) (This is what Python does when __nonzero__ method isn't defined). Bye, bearophile
Jun 10 2009
parent Leandro Lucarella <llucax gmail.com> writes:
bearophile, el 10 de junio a las 08:23 me escribiste:
 Don:
 It's very important. If you allow implicit conversion to bool, all kinds 
 of garbage will compile. You might as well abandon static typing.

Some half-backed ideas. opBool() can be called implicitly when it's required a truth value test, like in if(x), while(x || y), etc. So this produces a type error: bool b = x;

I just can't understand why someone would not want this conversion to work without a cast. Can anyone explain to me? -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- "All mail clients suck. This one just sucks less." -me, circa 1995
Jun 10 2009
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Don:
 The only time when you want to allow an implicit conversion to bool is 
 when you have some kind of "smart bool" struct which is supposed to be a 
 drop-in replacement for "bool". It's quite distinct from
 if(x) --> if(x!=0).

When the complex numbers are implemented by a struct in the standard library (or as in the original case of BigInts), I may like to keep being able to write: if (x) { ... } Where x is a Complex struct or BigInt struct that defines something like an opBool. In both those situations you can write: if (x != 0) { ... } They just need to define opEquals(int). Bye, bearophile
Jun 10 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
bearophile:
 When the complex numbers are implemented by a struct in the standard library
(or as in the original case of BigInts), I may like to keep being able to write:
 if (x) { ... }
 Where x is a Complex struct or BigInt struct that defines something like an
opBool.
 In both those situations you can write:
 if (x != 0) { ... }
 They just need to define opEquals(int).

It seems I was "right", C# too defines something like it, "true and false operators" (I don't know why they have both of them; isn't one enough?): http://www.java2s.com/Tutorial/CSharp/0160__Operator-Overload/truefalseoperatorforComplex.htm There's a certain number of C# features I'd like to see in D (like nullable types that act as nan of floating point numbers, etc). Bye, bearophile
Jun 22 2009
parent Sam <samhudotsamhu gmail.com> writes:
bearophile Wrote:

 
 There's a certain number of C# features I'd like to see in D (like nullable
types that act as nan of floating point numbers, etc).
 
 Bye,
 bearophile

Code snippet 1 in C#1: Address address =user.getContactAddress; if(address==null) { address=order.getShippingAddress; if(address==null) { address=user.getBillingAddress; } } Code snippet 2 in C#3 with ?? operator: Address address=user.getContactAddress?? user.getShippingAddress?? user.getBillingAddress; Regards, Sam
Jun 22 2009
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Don, el 10 de junio a las 03:50 me escribiste:
 Leandro Lucarella wrote:
Don, el 10 de junio a las 02:02 me escribiste:
bearophile wrote:
Leandro Lucarella:
I think the more general solution is to allow multiple implicit cast
operators and simply provide implicit conversion to bool for the classes
you want to be able to do if (x) on.




If you consider "also introduces bugs" as "more general"... I just call that "wrong" <g>.

No, I consider "not specialized or limited to one class of things"[1] as "more general". I'm not discussing if generality makes easy to introduce bugs in this case, that is what you are implying =) [1] Meaning taken from here http://wordnetweb.princeton.edu/perl/webwn?s=general (I've choose the meaning that better fit what I was trying to say)
C++ libraries go to a fair bit of trouble to allow if(x) without
allowing x to be converted to bool.

be useful (I didn't though about that).

It's very important. If you allow implicit conversion to bool, all kinds of garbage will compile. You might as well abandon static typing.

Again, I think this is a little too drastic.
 That makes me think, why not to
disable implicit cast from bool to int? It's even defined what the value
of int x = true; should be?

 I guess there are plenty of cases where you
don't want that, but I can't think of anything now...

I can't think of any case where it's a good idea. (It's required in D1 because opEquals() stupidly returns int).

I don't think is *required*, you can do an explicit cast, but it wouldn't be very nice indeed. BTW, wasn't that finally changed? Or it was a D2 only change?
 But that still leaves the problem:
 
 bool b = x; // doesn't compile if x is a class, or an array, or an AA.
 But it would compile if x is a struct with implicit conversion to bool.

I don't understand why that should not compile if the class/array/AA has an implicit conversion to bool defined.
Of course, int to bool implicit cast should still be possible.

No. It isn't legal now! Only the special case of literals 0 and 1. Which is perfect.

So is: int x; // ... if (x) // .. A special case? That's odd... I think it makes perfect sense to allow implicit conversion from int to bool. I don't think bool should be considered a "numeric type" to be concerned about the precision loss (or there is another reason not to allow that?).
 The only time when you want to allow an implicit conversion to bool is
 when you have some kind of "smart bool" struct which is supposed to be
 a drop-in replacement for "bool". It's quite distinct from
 if(x) --> if(x!=0).
 
 The strength of my proposal is that it would allow existing code to
 "just work" without modification. You wouldn't have to think about it.
 To argue against it, find a situation where it would be a problem.

I think implicit conversion to bool is a very common idiom to quickly ask if something is not "empty". Most languages do that... -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- De las generaciones venideras espero, nada más, que vengan. -- Ricardo Vaporeso
Jun 10 2009
parent Don <nospam nospam.com> writes:
Leandro Lucarella wrote:
 Don, el 10 de junio a las 03:50 me escribiste:
 Leandro Lucarella wrote:
 Don, el 10 de junio a las 02:02 me escribiste:
 bearophile wrote:
 Leandro Lucarella:
 I think the more general solution is to allow multiple implicit cast
 operators and simply provide implicit conversion to bool for the classes
 you want to be able to do if (x) on.





No, I consider "not specialized or limited to one class of things"[1] as "more general". I'm not discussing if generality makes easy to introduce bugs in this case, that is what you are implying =)

I'm implying that it does NOTHING other than introduce bugs. If you want to be pedantic: it does not reduce the limitations on useful behaviour.
 
 [1] Meaning taken from here
http://wordnetweb.princeton.edu/perl/webwn?s=general
     (I've choose the meaning that better fit what I was trying to say)
 
 C++ libraries go to a fair bit of trouble to allow if(x) without
 allowing x to be converted to bool.

be useful (I didn't though about that).

of garbage will compile. You might as well abandon static typing.

Again, I think this is a little too drastic.
 That makes me think, why not to
 disable implicit cast from bool to int? It's even defined what the value
 of int x = true; should be?

 I guess there are plenty of cases where you
 don't want that, but I can't think of anything now...

because opEquals() stupidly returns int).

I don't think is *required*, you can do an explicit cast, but it wouldn't be very nice indeed. BTW, wasn't that finally changed? Or it was a D2 only change?
 But that still leaves the problem:

 bool b = x; // doesn't compile if x is a class, or an array, or an AA.
 But it would compile if x is a struct with implicit conversion to bool.

I don't understand why that should not compile if the class/array/AA has an implicit conversion to bool defined.
 Of course, int to bool implicit cast should still be possible.


So is: int x; // ... if (x) // .. A special case? That's odd...

It's a special idiom, which you mention at the end of your post. It doesn't involve implicit conversion to bool. Thus avoiding bugs.
 I think it makes perfect sense to allow implicit conversion from int to
 bool. I don't think bool should be considered a "numeric type" to be
 concerned about the precision loss (or there is another reason not to
 allow that?).
 
 The only time when you want to allow an implicit conversion to bool is
 when you have some kind of "smart bool" struct which is supposed to be
 a drop-in replacement for "bool". It's quite distinct from
 if(x) --> if(x!=0).

 The strength of my proposal is that it would allow existing code to
 "just work" without modification. You wouldn't have to think about it.
 To argue against it, find a situation where it would be a problem.

I think implicit conversion to bool is a very common idiom to quickly ask if something is not "empty".

That is not correct. if(x) does NOT involve an implicit conversion to bool. That's the key issue.
Jun 26 2009
prev sibling parent Don <nospam nospam.com> writes:
bearophile wrote:
 Don:
 That is, if x is a struct, convert "if(x)" into "if(x==0)"
 and convert "if(!x)" into "if(x!=0)". Usual rules would then apply, so 
 any opEquals with a parameter which could be implicitly cast from 0 
 would work.
 That wouldn't allow smart-pointer structs to implement if (x) without 
 requiring them to allow comparisons with integers, so it might be 
 necessary to add a second step: try "if(x==null)" instead.

I don't like this solution, because it relies on a bit of invisible magic, and you too have seen it needs extensions because it's not general. If you want to use if (x) where x doesn't represent a number but something else like a collection, they you have to add even more special cases like add a third step: "if(x.length == 0)" etc. So this is not a good solution.

Currently, if(x) is a shortcut for either: if (x != 0) OR if (x !is null) depending on whether x is a value or a reference type. It never means anything else. I think it should stay that way. That is, if it is not legal to check it for equality with 0, the shortcut should not exist. And definitely, if "if(x!=0)" is a valid operation, it should never be different to "if(x)". BTW, in C++, it only ever means "x!=0", it's only because D treats null differently from 0 that the two cases exist.
Jun 09 2009
prev sibling parent Yigal Chripun <yigal100 gmail.com> writes:
Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 Andrei Alexandrescu wrote:
 Yigal Chripun wrote:
 your abstraction inversion example doesn't apply here. The problem I 
 see is the narrowing implicit cast, i.e. int values behave like 
 booleans. I have no problem with the reverse which is what your 
 example is about.

An int does not convert to bool implicitly. An int can be tested with "if", which is a different thing. Andrei

that is an implicit cast.

No. An implicit cast is this: int a; bool b = a; // doesn't compile or this: void fun(bool); fun(5); // doesn't compile You are mistakenly presupposing that if() takes a bool. In reality if() accepts a bool, an integral, a floating-point type, a pointer, an array, or a class reference. Andrei

I'm not debating terminology with you nor am I presupposing that if() currently takes a bool, I know it takes other types as well. what I am saying is that "if" needs to be fixed such that it _will_ take only bool. the the C idiom of: int a = ...; if (a) {...} is a bad pattern. it is even more problematic with floats. it _should_ be written always as: if (a == 0) { .. } zero is not false. in fact zero can be very positive: zero errors, zero cache misses, etc.
Jun 04 2009
prev sibling next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
bearophile wrote:
 Yigal Chripun:
 
 why not just use (i != 0) in both cases?

Yes, here it can be done (if Don has implemented opEquals(int y)), but you don't need that once you have something like opBool.
 conversion of ints to bools in C is IMO a hole in the type system
 due to the lack of a boolean type in C. All those narrowing
 implicit casts inherited from C are a bad idea IMO.

Walter was right, converting a generic integral to bool requires some time, that in the middle of the the nested loop can slow down code a bit (5%, in a case of mine). A standard opBool method allows you to tell when a collection is empty, like an empty string, an empty range, an empty array, and so on. you can use it instead of somecollection.isEmpty(). Java as Pascal keep booleans and integers separated, this looks tidy and clean. I love clean things, but in practice I don't think the current design of D (and C, Python, and several other languages) leads to a significant amount of bugs. It seems one of those situations where practical considerations win over purity. There are other situations where I'd like to see fixed some implicit type conversions. C# shows a saner design in such regard. There are few things in C# that I'd like to see copied by D.

more practical and/or useful than the Pascal approach. this is where we differ. The _only_ practical benefit I see is that it saves you two characters to type. on the other hand you get less readable code which can potentially be buggy. it's not important that the chance to get a bug here for an experienced C programmer (or a Python one) is slim since the comparison is against absolute zero chance of bugs with the pure approach. any small positive epsilon is still greater than zero.
 
 the need to convert the bigInt to long in order to print it is a
 design error in printf() - the format string should specify the
 formatting of the variables not their types.

printf was invented lot of time ago, in galaxy far away, when needs and situations were different. That code of mine is wrong, this is more correct, so here it's not printf fault: printf("%d\n", a[to!(long)i]); Because it's "i" the BigInt, not the contents of the "a" array. Bye, bearophile

Jun 02 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Yigal Chripun:
any small positive epsilon is still greater than zero.<

"Worse is better". An engineering system, as for example a computer language, is the result of lot of compromises. Better to keep yourself flexible. Bye, bearophile
Jun 02 2009
parent Yigal Chripun <yigal100 gmail.com> writes:
bearophile wrote:
 Yigal Chripun:
 any small positive epsilon is still greater than zero.<

"Worse is better". An engineering system, as for example a computer language, is the result of lot of compromises. Better to keep yourself flexible. Bye, bearophile

no. "Worse is better" implies a trade-off - you sacrifice something like purity, cleanliness, etc to gain simplicity of implementation. What I was trying to convey is that this does not apply here since there is no trade off to make - you get nothing by going with the worse option in this case.
Jun 03 2009
prev sibling parent reply Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Thu, Jun 4, 2009 at 2:20 PM, Denis Koroskin <2korden gmail.com> wrote:

 because C (nor D) standard doesn't guaranty that float(0) will be
 implemented as "all bits set to 0" on target platform (although it currently
 holds true).

Actually, D does. http://www.digitalmars.com/d/1.0/abi.html D requires floating-point types to be IEEE 754 compliant (though the current wording of 'real' might confusingly allow for other standards).
Jun 04 2009
parent reply BCS <ao pathlink.com> writes:
Reply to Jarrett,

 On Thu, Jun 4, 2009 at 2:20 PM, Denis Koroskin <2korden gmail.com>
 wrote:
 
 because C (nor D) standard doesn't guaranty that float(0) will be
 implemented as "all bits set to 0" on target platform (although it
 currently holds true).
 

http://www.digitalmars.com/d/1.0/abi.html D requires floating-point types to be IEEE 754 compliant (though the current wording of 'real' might confusingly allow for other standards).

I /think/ that it doesn't require that FP types be IEEE 754 types, just that they match the semantics (possibly with more accuracy). If you can assume that any FPU will be designed to work with IEEE 754 (would that be valid now days?), then you can assume that real will differ only by size. The only case I think you might need to look out for is where FP is done in software and at that point, what would real be anyway?
Jun 04 2009
parent Robert Fraser <fraserofthenight gmail.com> writes:
BCS wrote:
 If you can 
 assume that any FPU will be designed to work with IEEE 754 (would that 
 be valid now days?)

Not at all! IIRC, some of the PS2's CPUs don't implement NaN or Infinity (just check out the "Advanced" page of PCSX2, you can set how accurately the various PS2 CPUs FP operations are emulated on x86). Not sure if the PS2 is "now days," but... Also, this is kind of old, but it suggests GPUs have all sorts of different behavior: http://www.cs.unc.edu/~ibr/projects/paranoia/ ... When being used only for graphics, accuracy often isn't as important as speed, however with GPGPU, I wouldn't be surprised if newer GPUs were IEEE-compliant.
Jun 04 2009
prev sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Thu, 04 Jun 2009 22:10:58 +0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Max Samukha wrote:
 On Thu, 04 Jun 2009 10:41:31 -0500, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 You are mistakenly presupposing that if() takes a bool. In reality  
 if() accepts a bool, an integral, a floating-point type, a pointer, an  
 array, or a class reference.


I was sure I forgot something... and hash too. Anything that can be compared against 0 or null. Andrei

Is it considered a good practice? Technically, the following construct is not exactly portable: float f = ..; if (f) { } because C (nor D) standard doesn't guaranty that float(0) will be implemented as "all bits set to 0" on target platform (although it currently holds true). I believe it is better to use an epsilon, instead: if (fabsf(f) < epsilon) { } unless you need to compare strictly against 0: if (f == 0) { }
Jun 04 2009