www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - std.conv.to vs. casting

reply "CJS" <Prometheus85 hotmail.com> writes:
I'm having trouble understanding the difference between casting 
and std.conv.to. Any help?
Jul 03 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 4 July 2013 at 06:18:21 UTC, CJS wrote:
 I'm having trouble understanding the difference between casting 
 and std.conv.to. Any help?
Casting merely changes the "observed type", whereas "to" does a deep conversion. Observe: ---- import std.stdio; import std.conv; void main() { int[] ints = [1, 2, 3]; auto bytes1 = cast(ubyte[])(ints); auto bytes2 = to!(ubyte[])(ints); writeln(bytes1); writeln(bytes2); } ---- [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0] [1, 2, 3] ---- To is very useful to do "true" type conversion. It can change string width. Finally, "to" is able to do string interpretation. EG: ---- import std.stdio; import std.conv; void main() { int[] ints = to!(int[])("[1, 2, 3]"); writeln(ints); } ---- [1, 2, 3] ---- To is very convenient because it is a "one stop shop": It doesn't matter what you want to do: "to" will do it. You don't need to mix/match calls to atoi/itoa/encode/decode etc. Furthermore, it is safe: if anything fails, to will throw. It will also throw if you overflow: ---- import std.conv; void main() { int a = 500; ubyte b = cast(ubyte)(a); //No problem here: Silent overflow ubyte c = to!ubyte(a); //Runtime overflow exception } ---- Hope that helps :)
Jul 04 2013
parent reply Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
On 07/04/2013 10:14 AM, monarch_dodra wrote:
 Casting merely changes the "observed type", whereas "to" does a deep
conversion.
What are the speed implications of to compared to cast? I ask because I see various casts in Phobos, and wonder if there isn't improved safety in preferring instead to use to, so long as the optimal speed is there with appropriate compiler optimizations. I'm particularly concerned here because of a bug I observed that would have been caught if to had been used rather than cast: http://d.puremagic.com/issues/show_bug.cgi?id=10322#c4 (I'm working on a fix, it's just time constraints that have delayed delivering it:-)
Jul 04 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 4 July 2013 at 09:31:42 UTC, Joseph Rushton Wakeling 
wrote:
 On 07/04/2013 10:14 AM, monarch_dodra wrote:
 Casting merely changes the "observed type", whereas "to" does 
 a deep conversion.
What are the speed implications of to compared to cast? I ask because I see various casts in Phobos, and wonder if there isn't improved safety in preferring instead to use to, so long as the optimal speed is there with appropriate compiler optimizations. I'm particularly concerned here because of a bug I observed that would have been caught if to had been used rather than cast: http://d.puremagic.com/issues/show_bug.cgi?id=10322#c4 (I'm working on a fix, it's just time constraints that have delayed delivering it:-)
Speed implications, it mostly only means doing an "if (a > T.max)". It also means adding code to handle a throw. Finally, it means the function can't be marked nothrow. I'd argue that "to" should really only be used for legitimate cases where the runtime can create out of range values, and you want to catch that as an exception. Speed should not be a real issue*. I didn't go into the details, but in your bug report, it seems like it is more of an implementation error ? In that case, an ERROR would be better. *Speed wise, it is not a problem I'd say, except maybe in range primitives, espcially empty, front and opIndex, and to a certain, popFront. It depends on the relative cost of the operation of course. For example: Cycle provides RA indexing. the code is written as: "return source[(currentIndex + inputIndex) % length];" In this specific case, there is an overflow risk, even though RA access should be un-bounded. However, it is not checked, as the implications to calling to would be problematic. Placing an assert there is better, so release does not have to pay for the check.
Jul 04 2013
parent reply Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
On 07/04/2013 12:08 PM, monarch_dodra wrote:
 I didn't go into the details, but in your bug report, it seems like it is more
 of an implementation error ? In that case, an ERROR would be better.
The cast should be safe, as it's a size_t to a double. It's just that having to!double() would have identified a problem, and if the speed hit would be avoided with the use of -release -noboundscheck I'd have assumed it would be worth it. But you're right, the simplest thing to do is to fix the implementation error. Related note -- what would you anticipate about the speed hit of isNan(x) versus if(booleanValue) ... ? Yes, I'll benchmark. :-P
Jul 04 2013
next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 4 July 2013 at 10:15:22 UTC, Joseph Rushton Wakeling 
wrote:
 On 07/04/2013 12:08 PM, monarch_dodra wrote:
 I didn't go into the details, but in your bug report, it seems 
 like it is more
 of an implementation error ? In that case, an ERROR would be 
 better.
The cast should be safe, as it's a size_t to a double. It's just that having to!double() would have identified a problem, and if the speed hit would be avoided with the use of -release -noboundscheck I'd have assumed it would be worth it. But you're right, the simplest thing to do is to fix the implementation error. Related note -- what would you anticipate about the speed hit of isNan(x) versus if(booleanValue) ... ? Yes, I'll benchmark. :-P
I'm trying to read the bug entry, but I don't get it. Maybe a reduced case to explain? In any case, I don't think int to double ever throws so that wouldn't help...
Jul 04 2013
parent reply "Joseph Rushton Wakeling" <joseph.wakeling webdrake.net> writes:
On Thursday, 4 July 2013 at 10:23:22 UTC, monarch_dodra wrote:
 I'm trying to read the bug entry, but I don't get it. Maybe a 
 reduced case to explain?
Point is, the skip() function in std.random.RandomSample can be called with the floating-point variable _Vprime equal to nan, _if_ popFront() or index() are called before front(). If isNan(_Vprime) && !_algorithmA then skip() will generate incorrect results. I was asking about the speed of isNan compared to if(boolean) because I was considering killing the bool _first and just using isNan(_Vprime) to check if the first sample point has already been generated. I can prepare a reduced example but it's probably simpler for me to just prepare a bugfix and do some benchmarks.
Jul 04 2013
parent reply "Joseph Rushton Wakeling" <joseph.wakeling webdrake.net> writes:
On Thursday, 4 July 2013 at 10:32:26 UTC, Joseph Rushton Wakeling 
wrote:
 I can prepare a reduced example but it's probably simpler for 
 me to just prepare a bugfix and do some benchmarks.
By the way, CJS -- sorry to have hijacked your query. But I think you had your answer already :-)
Jul 04 2013
parent "CJS" <Prometheus85 hotmail.com> writes:
 By the way, CJS -- sorry to have hijacked your query.  But I 
 think you had your answer already :-)
Yes. It was a very helpful answer. I'm just glad the question I asked was apparently relevant to other users as well.
Jul 04 2013
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/04/2013 03:15 AM, Joseph Rushton Wakeling wrote:

 The cast should be safe, as it's a size_t to a double.
I am commenting without fully understanding the context: Both size_t and double are 64 bit types on a 64-bit system. double.mant_dig being 53, converting from size_t to double loses information for many values. import std.stdio; import std.conv; void main() { size_t i = 0x8000_0000_0000_0001; double d0 = i.to!double; double d1 = cast(double)i; writefln("%s", i); writefln("%f", d0); writefln("%f", d1); } Prints 9223372036854775809 9223372036854775808.000000 9223372036854775808.000000 Still, the same information loss in a comparison may make one think that it actually worked! :p assert(d0 == i); // passes! assert(d1 == i); // passes! Ali
Jul 04 2013
next sibling parent reply Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
On 07/04/2013 06:16 PM, Ali Çehreli wrote:
 I am commenting without fully understanding the context: Both size_t and double
 are 64 bit types on a 64-bit system. double.mant_dig being 53, converting from
 size_t to double loses information for many values.
Oh, bugger. You mean that because it needs space to store the exponent, it has a reduced number of significant figures compared to size_t?
 import std.stdio;
 import std.conv;
 
 void main()
 {
     size_t i = 0x8000_0000_0000_0001;
     double d0 = i.to!double;
     double d1 = cast(double)i;
 
     writefln("%s", i);
     writefln("%f", d0);
     writefln("%f", d1);
 }
 
 Prints
 
 9223372036854775809
 9223372036854775808.000000
 9223372036854775808.000000
Don't understand why the error arises here, as the number of significant figures is the same for all the numbers ... ?
Jul 04 2013
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/04/2013 09:43 AM, Joseph Rushton Wakeling wrote:

 On 07/04/2013 06:16 PM, Ali Çehreli wrote:
 I am commenting without fully understanding the context: Both size_t 
and double
 are 64 bit types on a 64-bit system. double.mant_dig being 53, 
converting from
 size_t to double loses information for many values.
Oh, bugger. You mean that because it needs space to store the
exponent, it has
 a reduced number of significant figures compared to size_t?
Exactly.
 import std.stdio;
 import std.conv;

 void main()
 {
      size_t i = 0x8000_0000_0000_0001;
      double d0 = i.to!double;
      double d1 = cast(double)i;

      writefln("%s", i);
      writefln("%f", d0);
      writefln("%f", d1);
 }

 Prints

 9223372036854775809
 9223372036854775808.000000
 9223372036854775808.000000
Don't understand why the error arises here, as the number of
significant figures
 is the same for all the numbers ... ?
It is about the difference between the significant number of bits. The size_t value above uses bits 63 and 0, a range of 64 bits. double's 53-bit mantissa has no room for bit 0. In contrast, the following size_t value can be represented exactly in a double because its representation uses only 53 bits: import std.stdio; import std.conv; void main() { size_t i = 0x001f_ffff_ffff_ffff; double d = i.to!double; writefln("%s", i); writefln("%f", d); } Prints: 9007199254740991 9007199254740991.000000 However, set any one of the currently-unset upper 11 bits (64 - 53 == 11), and you get an inexact conversion. Ali
Jul 04 2013
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Jul 04, 2013 at 06:43:16PM +0200, Joseph Rushton Wakeling wrote:
 On 07/04/2013 06:16 PM, Ali ehreli wrote:
 I am commenting without fully understanding the context: Both size_t
 and double are 64 bit types on a 64-bit system. double.mant_dig
 being 53, converting from size_t to double loses information for
 many values.
Oh, bugger. You mean that because it needs space to store the exponent, it has a reduced number of significant figures compared to size_t?
[...] Yes. See: http://en.wikipedia.org/wiki/Double-precision_floating-point_format Of the 64 bits, only 53 are available for storing the mantissa (well, actually 52, but the first bit is always 1 except when the exponent is zero so it's not stored -- you get it for free). Of the remaining bits, 11 are reserved for storing the exponent, and the last for storing the sign. So the maximum precision a double can have is 53 bits. If you have a value that requires more than that, the representation will be inexact. T -- Programming is not just an act of telling a computer what to do: it is also an act of telling other programmers what you wished the computer to do. Both are important, and the latter deserves care. -- Andrew Morton
Jul 04 2013
prev sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 4 July 2013 at 16:16:08 UTC, Ali Çehreli wrote:
 On 07/04/2013 03:15 AM, Joseph Rushton Wakeling wrote:

 The cast should be safe, as it's a size_t to a double.
I am commenting without fully understanding the context: Both size_t and double are 64 bit types on a 64-bit system. double.mant_dig being 53, converting from size_t to double loses information for many values.
It's not about losing information, it's about being out of range. For example, to!float(2.0^^50) will throw an exception.
Jul 04 2013