www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Is there any reason why arithmetic operation on shorts and bytes

reply d coder <dlang.coder gmail.com> writes:
Greetings

The following code prints FFFF0000. Similar thing happens when a and b are
bytes.
Is this intended?

Regards
- Puneet

void main()
{
  import std.stdio;
  ushort a = 0x55AA;
  ushort b = 0xAA55;
  writefln("%X", ~(a | b));
}
Dec 11 2012
next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Tuesday, 11 December 2012 at 12:24:14 UTC, d coder wrote:
 Greetings

 The following code prints FFFF0000. Similar thing happens when 
 a and b are
 bytes.
 Is this intended?

 Regards
 - Puneet

 void main()
 {
   import std.stdio;
   ushort a = 0x55AA;
   ushort b = 0xAA55;
   writefln("%X", ~(a | b));
 }
integer operations are always promoted to at least int. That's standard fare since C. I think it is a performance thing: Unpack into ints, oeprate, repack into ints. D's stance regarding integer operations is "if it compiles, it creates the same output as in C". It's kind of a gotcha, but not that big a deal.
Dec 11 2012
next sibling parent reply d coder <dlang.coder gmail.com> writes:
On Tue, Dec 11, 2012 at 6:23 PM, monarch_dodra <monarchdodra gmail.com>wrote:

 D's stance regarding integer operations is "if it compiles, it creates the
 same output as in C".
Thanks Monarch. So it is a gotcha we inherit from C. :-) Regards - Puneet
Dec 11 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 12/11/12 8:03 AM, d coder wrote:
 On Tue, Dec 11, 2012 at 6:23 PM, monarch_dodra <monarchdodra gmail.com
 <mailto:monarchdodra gmail.com>> wrote:

     D's stance regarding integer operations is "if it compiles, it
     creates the same output as in C".


 Thanks Monarch. So it is a gotcha we inherit from C. :-)
No, it's a fix of a gotcha from C. The C code would just allow the assignment. Andrei
Dec 11 2012
parent reply d coder <dlang.coder gmail.com> writes:
 No, it's a fix of a gotcha from C. The C code would just allow the
 assignment.
Yes Andrei. But it does not look clean if you have to write: byte a, b, c; a = cast(byte) (b + c); Well I know the advantages (safety). But imagine having to write all that when working with bytes and shorts. Makes it really difficult to work with shorts and bytes in D. Would I be asking for too much if I ask DMD to provide a compiler flag that makes it return bytes and shorts for operations on them? So the deal would be, if you use this compiler flag, code behavior would be different from that of C/C++. What we get would be more practical short and byte behavior. Regards - Puneet
Dec 11 2012
next sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Tuesday, 11 December 2012 at 13:24:59 UTC, d coder wrote:

 But it does not look clean if you have to write:

 byte a, b, c;
 a = cast(byte) (b + c);
That's the whole point. What you are doing is dangerous, so it requires the cast. Adding a compiler flag to change the semantics of the language sounds like a recipe for disaster.
Dec 11 2012
parent reply d coder <dlang.coder gmail.com> writes:
On Tue, Dec 11, 2012 at 7:05 PM, Peter Alexander <
peter.alexander.au gmail.com> wrote:

 That's the whole point. What you are doing is dangerous, so it requires
 the cast.
What I am doing is not dangerous. I am operating at byte/short level. Tell me, if what I am doing is dangerous, how come doing the following is not dangerous. It is allowed by D. int a, b, c; a = b + c; Also the sense of safety that you get for short and byte too is not complete. Consider: void main() { import std.stdio; ushort a = ushort.max; ushort b = ushort.max; a += b; writeln(a); } Is it too much to ask for consistent behavior across the built-in types? Regards - Puneet
Dec 11 2012
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
d coder:

 Tell me, if what I am doing is dangerous, how come doing the 
 following is not dangerous. It is allowed by D.

 int a, b, c;
 a = b + c;
I think this dis-uniformity was added because: - Statistically it's more common to have an overflow when you sum two bytes compared to summing two ints. - If the sum of two ints/uints requires a cast, you need to fill your code with casts. And casts are not safe at all, they are sharp powertools. (I have asked many times for runtime integral overflows in D (as But D designers come from the great unsafety of C-like languages and do not agree with me on the dangers/bugs of fixnum operations). Bye, bearophile
Dec 11 2012
prev sibling parent reply "js.mdnq" <js_adddot+mdng gmail.com> writes:
On Tuesday, 11 December 2012 at 14:02:52 UTC, d coder wrote:
 On Tue, Dec 11, 2012 at 7:05 PM, Peter Alexander <
 peter.alexander.au gmail.com> wrote:

 That's the whole point. What you are doing is dangerous, so it 
 requires
 the cast.
What I am doing is not dangerous. I am operating at byte/short level. Tell me, if what I am doing is dangerous, how come doing the following is not dangerous. It is allowed by D. int a, b, c; a = b + c; Also the sense of safety that you get for short and byte too is not complete. Consider: void main() { import std.stdio; ushort a = ushort.max; ushort b = ushort.max; a += b; writeln(a); } Is it too much to ask for consistent behavior across the built-in types? Regards - Puneet
What if you create a new type that creates the proper functionality and does the implicit casting and such? This would hide away the details but maybe at a cost of efficiency. e.g., struct bbyte { byte value; ... } bbyte a; bbyte b; b = a + b; // uses bbyte's operators and casts to do the computation and assignment but then returns a bbyte instead of an int. You should have no problems implicitly converting bbyte to built in types or built in types to bbyte.
Dec 11 2012
next sibling parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2012-09-12 00:12, js.mdnq <js_adddot+mdng gmail.com> wrote:

 struct bbyte {
 byte value;
 ...
 }

 bbyte a; bbyte b;

 b = a + b; // uses bbyte's operators and casts to do the computation and  
 assignment but then returns a bbyte instead of an int.

 You should have no problems implicitly converting bbyte to built in  
 types or built in types to bbyte.
Not entirely true. Converting from bbyte to built-in works, but these are to my knowledge currently impossible: void foo(bbyte b); byte b; foo(b); // No conversion. bbyte bar( ) { byte b; return b; } -- Simen
Dec 12 2012
parent "js.mdnq" <js_adddot+mdng gmail.com> writes:
On Wednesday, 12 December 2012 at 08:23:48 UTC, Simen Kjaeraas 
wrote:
 On 2012-09-12 00:12, js.mdnq <js_adddot+mdng gmail.com> wrote:

 struct bbyte {
 byte value;
 ...
 }

 bbyte a; bbyte b;

 b = a + b; // uses bbyte's operators and casts to do the 
 computation and assignment but then returns a bbyte instead of 
 an int.

 You should have no problems implicitly converting bbyte to 
 built in types or built in types to bbyte.
Not entirely true. Converting from bbyte to built-in works, but these are to my knowledge currently impossible: void foo(bbyte b); byte b; foo(b); // No conversion. bbyte bar( ) { byte b; return b; }
What I mean is that you use bbyte for all your arithmetic operations and computations. When you need to convert it to a byte(hopefully at the end of the process), you can use an implicit cast. struct bbyte { byte b; byte opCast(bbyte a) { return a.b; } alias b this; bbyte bar(byte b) { bbyte q; q.b = b; return q; } byte foo(bbyte b) { return b.b; } } byte bar2(byte b) { return b; } int main(string[] argv) { bbyte bb; byte b; bb.foo(bb); bb.bar(b); b = bb; bb = b; } the point is no explicit op casts are required. Essentially a bbyte is a byte. One can overload all the operators that are needed for bit manipulation, even using asm if necessary and one should be able to hide most of the details. `alias this` might not stop some of the value propagation in some cases. If one only converts to the built-in types when actually needed(when passing to and from routines that use them) but uses bbyte for all bit manipulations it shouldn't be much of a problem(if at all). The idea is that conversion from bbyte to byte does not induce an expansion to int, only the arithmetic operations. Hence, overriding them can stop that. but something like byte b1; byte b2; bbyte b3 = b1 + b2; is computing the arithmetic operation from byte(which converts to an int). This is why I say one must use bbytes for all arithmetic operations to solve that problem. (or write a cast for int, if you just don't want to deal with it but don't mind the expansion) But this works: byte b1; byte b2; bbyte b3; b3 = b1 + b2; if one has bbyte opAssign(int i) { this.b = cast(byte)i; return this; } in bbyte. which avoids having to do b3 = cast(byte)(b1 + b2) which I think was what the original post was about.
Dec 12 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, December 12, 2012 09:23:35 Simen Kjaeraas wrote:
 On 2012-09-12 00:12, js.mdnq <js_adddot+mdng gmail.com> wrote:
 struct bbyte {
 byte value;
 ...
 }
 
 bbyte a; bbyte b;
 
 b = a + b; // uses bbyte's operators and casts to do the computation and
 assignment but then returns a bbyte instead of an int.
 
 You should have no problems implicitly converting bbyte to built in
 types or built in types to bbyte.
Not entirely true. Converting from bbyte to built-in works, but these are to my knowledge currently impossible: void foo(bbyte b); byte b; foo(b); // No conversion. bbyte bar( ) { byte b; return b; }
You can do it with alias this, but the current lack of ability to have multiple alias thises probably makes it so that you can't use it for converting in both directions unless you want to directly alias it to the member variable holding the value (which eans no checks or whatever else you might want to do in bbyte). Once we can have multiple alias thises though, that shouldn't be problem anymore. - Jonathan M Davis
Dec 12 2012
prev sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2012-17-12 10:12, Jonathan M Davis <jmdavisProg gmx.com> wrote:

 On Wednesday, December 12, 2012 09:23:35 Simen Kjaeraas wrote:
 On 2012-09-12 00:12, js.mdnq <js_adddot+mdng gmail.com> wrote:
 struct bbyte {
 byte value;
 ...
 }

 bbyte a; bbyte b;

 b = a + b; // uses bbyte's operators and casts to do the computation  
and
 assignment but then returns a bbyte instead of an int.

 You should have no problems implicitly converting bbyte to built in
 types or built in types to bbyte.
Not entirely true. Converting from bbyte to built-in works, but these are to my knowledge currently impossible: void foo(bbyte b); byte b; foo(b); // No conversion. bbyte bar( ) { byte b; return b; }
You can do it with alias this, but the current lack of ability to have multiple alias thises probably makes it so that you can't use it for converting in both directions unless you want to directly alias it to the member variable holding the value (which eans no checks or whatever else you might want to do in bbyte). Once we can have multiple alias thises though, that shouldn't be problem anymore.
Really? This certainly does not compile for me: struct bbyte { byte b; alias b this; } void bar(bbyte b) {} bbyte baz() { byte b; return b; // cannot implicitly convert expression (b) of type byte to bbyte } void main() { byte b; bar(b); // function bar (bbyte b) is not callable using argument types (byte) } http://d.puremagic.com/issues/show_bug.cgi?id=8570 -- Simen
Dec 12 2012
prev sibling next sibling parent "Rene Zwanenburg" <renezwanenburg gmail.com> writes:
On Tuesday, 11 December 2012 at 13:24:59 UTC, d coder wrote:
 No, it's a fix of a gotcha from C. The C code would just allow 
 the
 assignment.
Yes Andrei. But it does not look clean if you have to write: byte a, b, c; a = cast(byte) (b + c); Well I know the advantages (safety). But imagine having to write all that when working with bytes and shorts. Makes it really difficult to work with shorts and bytes in D. Would I be asking for too much if I ask DMD to provide a compiler flag that makes it return bytes and shorts for operations on them? So the deal would be, if you use this compiler flag, code behavior would be different from that of C/C++. What we get would be more practical short and byte behavior. Regards - Puneet
If the compiler can statically verify that some integer value fits in a byte or short, you don't need the cast. So to rewrite your example: byte a, b, c; a = (b + c) & 0xFF; Also: short a; byte b, c; a = b + c; Still not exactly what you're looking for, but the mask looks nicer than a cast IMO. It's also safer.
Dec 11 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 12/11/12 8:24 AM, d coder wrote:
     No, it's a fix of a gotcha from C. The C code would just allow the
     assignment.


 Yes Andrei.

 But it does not look clean if you have to write:

 byte a, b, c;
 a = cast(byte) (b + c);

 Well I know the advantages (safety). But imagine having to write all
 that when working with bytes and shorts. Makes it really difficult to
 work with shorts and bytes in D.
Value range propagation automatically avoids the need for a lot of those casts. http://www.drdobbs.com/tools/value-range-propagation/229300211
 Would I be asking for too much if I ask
 DMD to provide a compiler flag that makes it return bytes and shorts for
 operations on them?
That won't happen. Andrei
Dec 11 2012
next sibling parent reply d coder <dlang.coder gmail.com> writes:
Thanks Everybody for responding. I have another query which is off-topic
but related.

Why is the following allowed in D?

long a;
int b;

b += a; // Allowed -- no explicit cast
b = a + b; // Not allowed
b = a;      // Not allowed
Dec 11 2012
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
d coder:

 Why is the following allowed in D?

 long a;
 int b;

 b += a; // Allowed -- no explicit cast
 b = a + b; // Not allowed
 b = a;      // Not allowed
Seems a bug of range analysis. Bye, bearophile
Dec 11 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 12/11/12 11:36 AM, d coder wrote:
 Thanks Everybody for responding. I have another query which is off-topic
 but related.

 Why is the following allowed in D?

 long a;
 int b;

 b += a; // Allowed -- no explicit cast
RMW operations are allowed without a cast. Andrei
Dec 11 2012
prev sibling parent "foobar" <foo bar.com> writes:
On Tuesday, 11 December 2012 at 16:09:14 UTC, Andrei Alexandrescu 
wrote:
 On 12/11/12 8:24 AM, d coder wrote:
    No, it's a fix of a gotcha from C. The C code would just 
 allow the
    assignment.


 Yes Andrei.

 But it does not look clean if you have to write:

 byte a, b, c;
 a = cast(byte) (b + c);

 Well I know the advantages (safety). But imagine having to 
 write all
 that when working with bytes and shorts. Makes it really 
 difficult to
 work with shorts and bytes in D.
Value range propagation automatically avoids the need for a lot of those casts. http://www.drdobbs.com/tools/value-range-propagation/229300211
 Would I be asking for too much if I ask
 DMD to provide a compiler flag that makes it return bytes and 
 shorts for
 operations on them?
That won't happen. Andrei
But than we have a bug in the value range propagation algorithm. E.g in the original program: void main() { import std.stdio; ushort a = 0x55AA; ushort b = 0xAA55; writefln("%X", ~(a | b)); } (a | b) will be in the range of ushort values and ~ of that also remains in the same value range. In other words, it makes no sense that boolean ops (and, or, xor, 1's compliment, 2's compliment) will require type promotion as they cannot exceed the original width of the values. This is unlike arithmetic which can require promotion.
Dec 11 2012
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 12/11/2012 5:24 AM, d coder wrote:
 Would I be asking for too much if I ask DMD to provide a compiler
 flag that makes it return bytes and shorts for operations on them? So the deal
 would be, if you use this compiler flag, code behavior would be different from
 that of C/C++.
Having compiler flags that fundamentally change the behavior of the language are a disaster from about every point of view.
Dec 11 2012
parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Tue, 11 Dec 2012 09:47:33 -0800
Walter Bright <newshound2 digitalmars.com> wrote:

 On 12/11/2012 5:24 AM, d coder wrote:
 Would I be asking for too much if I ask DMD to provide a compiler
 flag that makes it return bytes and shorts for operations on them?
 So the deal would be, if you use this compiler flag, code behavior
 would be different from that of C/C++.
Having compiler flags that fundamentally change the behavior of the language are a disaster from about every point of view.
Yea, I'd hate to see D turn into PHP. That's one of the biggest reasons I despise PHP - code can't rely on fucking *anything* in it to ever work in any particular way. Pick any random line of PHP code, and there's about 20 different things it might do, even on the exact same version of PHP, all depending on a billion different configuration settings. Any library written in PHP is *inherently* broken simply by being one codebase intending to be "usable" on different servers. Let's not entertain any thoughts of allowing D to venture down that path.
Dec 11 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 12/11/2012 10:12 AM, Nick Sabalausky wrote:
 Let's not entertain any thoughts of allowing D to venture down that
 path.
No worries there :-) I feel pretty dang strongly about this issue, from bad experience. Even if a language behaves "wrong", it is still usable if it is predictable.
Dec 11 2012
parent reply d coder <dlang.coder gmail.com> writes:
On Wed, Dec 12, 2012 at 3:40 AM, Walter Bright
<newshound2 digitalmars.com>wrote:

 No worries there :-) I feel pretty dang strongly about this issue, from
 bad experience.

 Even if a language behaves "wrong", it is still usable if it is
 predictable.
Agreed. How about this. 1. Let "char" and "short" behave the C/C++ way. These can return integers. This will make C/C++ code work in D. 2. Make D "byte" operations return bytes. And create another type (named say dbyte or shortint) and make operations on the new type return shortints. This will make these types more usable in D code. Well we end up adding another keyword, but we shall get workable and safe 8-bit and 16-bit integrals. Regards - Puneet
Dec 11 2012
parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Wednesday, 12 December 2012 at 03:10:15 UTC, d coder wrote:
 On Wed, Dec 12, 2012 at 3:40 AM, Walter Bright
 <newshound2 digitalmars.com>wrote:

 No worries there :-) I feel pretty dang strongly about this 
 issue, from
 bad experience.

 Even if a language behaves "wrong", it is still usable if it is
 predictable.
Agreed. How about this. 1. Let "char" and "short" behave the C/C++ way. These can return integers. This will make C/C++ code work in D. 2. Make D "byte" operations return bytes. And create another type (named say dbyte or shortint) and make operations on the new type return shortints. This will make these types more usable in D code. Well we end up adding another keyword, but we shall get workable and safe 8-bit and 16-bit integrals. Regards - Puneet
What's now is safer than what you propose. You can always create your own data type that would automatically truncate result if you wish so.
Dec 11 2012
parent d coder <dlang.coder gmail.com> writes:
On Wed, Dec 12, 2012 at 9:07 AM, Denis Koroskin <2korden gmail.com> wrote:

 What's now is safer than what you propose. You can always create your own
 data type that would automatically truncate result if you wish so.
:-) I should not have used the word "safe" there. All I meant was "safer" than C++ which allows implicit conversion form int to short and long to int. Yes, I am now using my own User Type. Regards - Puneet
Dec 11 2012
prev sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Tuesday, 11 December 2012 at 12:53:38 UTC, monarch_dodra wrote:
 integer operations are always promoted to at least int. That's 
 standard fare since C. I think it is a performance thing: 
 Unpack into ints, oeprate, repack into ints.
If memory serves me right, promotion to int is based more on CPU efficiency and code size than anything else (and yes packing and unpacking takes some 6 extra steps). Also with libraries possibly written by different compilers may have different results if they don't all agree on a common (default) type to return. Also most x86 operations for ints can be written as 1 byte opcodes, although for modern CPUs that kind of code generation may not be as efficient as it used to be. http://www.codeproject.com/Articles/6154/Writing-Efficient-C-and-C-Code-Optimization
Dec 16 2012
prev sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, December 12, 2012 10:42:31 Simen Kjaeraas wrote:
 Really? This certainly does not compile for me:
 
 
 struct bbyte {
 byte b;
 alias b this;
 }
 
 void bar(bbyte b) {}
 
 bbyte baz() {
 byte b;
 return b; // cannot implicitly convert expression (b) of type byte to
 bbyte
 }
 
 void main() {
 byte b;
 bar(b); // function bar (bbyte b) is not callable using argument types
 (byte)
 }
 
 

 
 http://d.puremagic.com/issues/show_bug.cgi?id=8570
If alias this isn't do an implict conversion, then there's a bug in alias this. That's how implict conversion is done in D, and it's the whole point of alias this. - Jonathan M Davis
Dec 12 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 12/12/2012 10:25 AM, Jonathan M Davis wrote:
 If alias this isn't do an implict conversion, then there's a bug in alias
 this. That's how implict conversion is done in D, and it's the whole point
 of alias this.
And it does, as I relied on this to do the halffloat implementation.
Dec 12 2012
next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, December 12, 2012 13:35:59 Walter Bright wrote:
 On 12/12/2012 10:25 AM, Jonathan M Davis wrote:
 If alias this isn't do an implict conversion, then there's a bug in alias
 this. That's how implict conversion is done in D, and it's the whole point
 of alias this.
And it does, as I relied on this to do the halffloat implementation.
Simen's example doesn't seem to work though, so he appears to have found a bug. Certainly, I don't see anything wrong with it, but alias this doesn't seem to work for it. Regardless, my point was that if there's a case where alias this isn't doing an implicit conversion, then it's a bug, because that's the whole reason that it exists. - Jonathan M Davis
Dec 12 2012
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2012-22-13 04:12, Jonathan M Davis <jmdavisProg gmx.com> wrote:

 On Wednesday, December 12, 2012 13:35:59 Walter Bright wrote:
 On 12/12/2012 10:25 AM, Jonathan M Davis wrote:
 If alias this isn't do an implict conversion, then there's a bug in  
alias
 this. That's how implict conversion is done in D, and it's the whole  
point
 of alias this.
And it does, as I relied on this to do the halffloat implementation.
Simen's example doesn't seem to work though, so he appears to have found a bug. Certainly, I don't see anything wrong with it, but alias this doesn't seem to work for it. Regardless, my point was that if there's a case where alias this isn't doing an implicit conversion, then it's a bug, because that's the whole reason that it exists.
So it is supposed to work? I've always thought this was a deliberate design choice. I guess I'll file a bug, then. Here: http://d.puremagic.com/issues/show_bug.cgi?id=9147 Kenji, you here? :p -- Simen
Dec 13 2012
prev sibling next sibling parent kenji hara <k.hara.pg gmail.com> writes:
D does not support such implicit *construction* in return statement and
function argument.
It is a current language design, and not a bug.

Kenji Hara


2012/12/13 Simen Kjaeraas <simen.kjaras gmail.com>

 On 2012-22-13 04:12, Jonathan M Davis <jmdavisProg gmx.com> wrote:

  On Wednesday, December 12, 2012 13:35:59 Walter Bright wrote:
 On 12/12/2012 10:25 AM, Jonathan M Davis wrote:
 If alias this isn't do an implict conversion, then there's a bug in
alias
 this. That's how implict conversion is done in D, and it's the whole
point
 of alias this.
And it does, as I relied on this to do the halffloat implementation.
Simen's example doesn't seem to work though, so he appears to have found a bug. Certainly, I don't see anything wrong with it, but alias this doesn't seem to work for it. Regardless, my point was that if there's a case where alias this isn't doing an implicit conversion, then it's a bug, because that's the whole reason that it exists.
So it is supposed to work? I've always thought this was a deliberate design choice. I guess I'll file a bug, then. Here: http://d.puremagic.com/issues/**show_bug.cgi?id=9147<http://d.puremagic.com/issues/show_bug.cgi?id=9147> Kenji, you here? :p -- Simen
Dec 13 2012
prev sibling parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2012-51-13 13:12, kenji hara <k.hara.pg gmail.com> wrote:

 D does not support such implicit *construction* in return statement and
 function argument.
 It is a current language design, and not a bug.
Walter does not seem to agree (see his post in this discussion). Previous indicates he also thinks alias this (or some other language feature) should support this. I guess this is not really relevant to this discussion, so I'll make a separate thread. [1]: http://d.puremagic.com/issues/show_bug.cgi?id=8570 [2]: http://forum.dlang.org/thread/sedknwtlaefrxuflnbez forum.dlang.org?page=8#postjul0qv:242l9d:241:40digitalmars.com -- Simen
Dec 13 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 12/13/2012 6:30 AM, Simen Kjaeraas wrote:
 Walter does not seem to agree (see his post in this discussion).
Note that the following implementation of halffloat does work, allowing explicit cast to halffloat and implicit conversion from. (The halffloat literals don't work at the moment because of a limitation in CTFE, I'm working with Don to resolve that.) ----------------------------------------------------------- /* * References: * http://en.wikipedia.org/wiki/Half-precision_floating-point_format */ module halffloat; struct HF { /* Provide implicit conversion of HF to float */ property float toFloat() { return shortToFloat(s); } alias toFloat this; /* Done as a template in order to prevent implicit conversion * of argument to float. */ this(T : float)(T f) { static assert(is(T == float)); s = floatToShort(f); } /* These are done as properties to avoid * circular reference problems. */ static property HF min_normal() { HF hf = void; hf.s = 0x0400; return hf; /* fp16!0x1p-14; */ } static property HF max() { HF hf = void; hf.s = 0x7BFF; return hf; /* fp16!0x1.FFCp+15; */ } static property HF nan() { HF hf = void; hf.s = EXPMASK | 1; return hf; /* fp16!(float.nan); */ } static property HF infinity() { HF hf = void; hf.s = EXPMASK; return hf; /* fp16!(float.infinity); */ } static property HF epsilon() { HF hf = void; hf.s = 0x3C01; return hf; /* fp16!0x1p-10; */ } enum dig = 3; enum mant_dig = 11; enum max_10_exp = 5; enum max_exp = 16; enum min_10_exp = -5; enum min_exp = -14; private: ushort s = EXPMASK | 1; // .init is HF.nan } /******************** * User defined literal for Half Float. */ template fp16(float v) { enum fp16 = HF(v); } private: // Half float values enum SIGNMASK = 0x8000; enum EXPMASK = 0x7C00; enum MANTMASK = 0x03FF; enum HIDDENBIT = 0x0400; // float values enum FSIGNMASK = 0x80000000; enum FEXPMASK = 0x7F800000; enum FMANTMASK = 0x007FFFFF; enum FHIDDENBIT = 0x00800000; // Rounding mode enum ROUND { TONEAREST, UPWARD, DOWNWARD, TOZERO }; enum ROUNDMODE = ROUND.TONEAREST; union U { uint u; float f; } ushort floatToShort(float f) { /* If the target CPU has a conversion instruction, this code could be * replaced with inline asm or a compiler intrinsic, but leave this * as the CTFE path so CTFE can work on it. */ /* The code currently does not set INEXACT, UNDERFLOW, or OVERFLOW, * but is marked where those would go. */ U uf = void; uf.f = f; uint s = uf.u; ushort u = (s & FSIGNMASK) ? SIGNMASK : 0; int exp = s & FEXPMASK; if (exp == FEXPMASK) // if nan or infinity { if ((s & FMANTMASK) == 0) // if infinity { u |= EXPMASK; } else // else nan { u |= EXPMASK | 1; } return u; } uint significand = s & FMANTMASK; if (exp == 0) // if subnormal or zero { if (significand == 0) // if zero return u; /* A subnormal float is going to give us a zero result anyway, * so just set UNDERFLOW and INEXACT and return +-0. */ return u; } else // else normal { // normalize exponent and remove bias exp = (exp >> 23) - 127; significand |= FHIDDENBIT; } exp += 15; // bias the exponent bool guard = false; // guard bit bool sticky = false; // sticky bit uint shift = 13; // lop off rightmost 13 bits if (exp <= 0) // if subnormal { shift += -exp + 1; // more bits to lop off exp = 0; } if (shift > 23) { // Set UNDERFLOW, INEXACT, return +-0 return u; } // Lop off rightmost 13 bits, but save guard and sticky bits guard = (significand & (1 << (shift - 1))) != 0; sticky = (significand & ((1 << (shift - 1)) - 1)) != 0; significand >>= shift; if (guard || sticky) { // Lost some bits, so set INEXACT and round the result switch (ROUNDMODE) { case ROUND.TONEAREST: if (guard && (sticky || (significand & 1))) ++significand; break; case ROUND.UPWARD: if (!(s & FSIGNMASK)) ++significand; break; case ROUND.DOWNWARD: if (s & FSIGNMASK) ++significand; break; case ROUND.TOZERO: break; default: assert(0); } if (exp == 0) // if subnormal { if (significand & HIDDENBIT) // and not a subnormal no more ++exp; } else if (significand & (HIDDENBIT << 1)) { significand >>= 1; ++exp; } } if (exp > 30) { // Set OVERFLOW and INEXACT, return +-infinity return u | EXPMASK; } /* Add exponent and significand into result. */ u |= exp << 10; // exponent u |= (significand & ~HIDDENBIT); // significand return u; } float shortToFloat(ushort s) { /* If the target CPU has a conversion instruction, this code could be * replaced with inline asm or a compiler intrinsic, but leave this * as the CTFE path so CTFE can work on it. */ /* This one is fairly easy because there are no possible errors * and no necessary rounding. */ int exp = s & EXPMASK; if (exp == EXPMASK) // if nan or infinity { float f; if ((s & MANTMASK) == 0) // if infinity { f = float.infinity; } else // else nan { f = float.nan; } return (s & SIGNMASK) ? -f : f; } uint significand = s & MANTMASK; if (exp == 0) // if subnormal or zero { if (significand == 0) // if zero return (s & SIGNMASK) ? -0.0f : 0.0f; // Normalize by shifting until the hidden bit is 1 while (!(significand & HIDDENBIT)) { significand <<= 1; --exp; } significand &= ~HIDDENBIT; // hidden bit is, well, hidden exp -= 14; } else // else normal { // normalize exponent and remove bias exp = (exp >> 10) - 15; } /* Assemble sign, exponent, and significand into float. * Don't have to deal with overflow, inexact, or subnormal * because the range of floats is big enough. */ assert(-126 <= exp && exp <= 127); // just to be sure //printf("exp = %d, significand = x%x\n", exp, significand); uint u = (s & SIGNMASK) << 16; // sign bit u |= (exp + 127) << 23; // bias the exponent and shift into position u |= significand << (23 - 10); U uf = void; uf.u = u; return uf.f; } import std.stdio; void main() { // HF h = fp16!27.2f; // HF j = cast(HF)( fp16!3.5f + fp16!5 ); HF f = HF(0.0f); f.s = 0x3C00; writeln("1 ", cast(float)f); f.s = 0x3C01; writeln("1.0009765625 ", cast(float)f); assert(f == HF.epsilon); f.s = 0xC000; writeln("-2 ", cast(float)f); f.s = 0x7BFF; writeln("65504 ", cast(float)f); assert(f == HF.max); f.s = 0x0400; writeln("6.10352e-5 ", cast(float)f); assert(f == HF.min_normal); f.s = 0x03FF; writeln("6.09756e-5 ", cast(float)f); f.s = 1; writeln("5.96046e-8 ", cast(float)f); f.s = 0; writeln("0 ", cast(float)f); assert(f == 0.0f); f.s = 0x8000; writeln("-0 ", cast(float)f); assert(f == -0.0f); f.s = 0x7C00; writeln("infinity ", cast(float)f); assert(f == HF.infinity); f.s = 0xFC00; writeln("-infinity ", cast(float)f); assert(f == -HF.infinity); f.s = 0x3555; writeln("0.33325 ", cast(float)f); }
Dec 13 2012
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Dec 13, 2012 at 05:44:23PM -0800, Walter Bright wrote:
 On 12/13/2012 6:30 AM, Simen Kjaeraas wrote:
Walter does not seem to agree (see his post in this discussion).
Note that the following implementation of halffloat does work, allowing explicit cast to halffloat and implicit conversion from. (The halffloat literals don't work at the moment because of a limitation in CTFE, I'm working with Don to resolve that.) -----------------------------------------------------------
Nice!! [...]
     /* Provide implicit conversion of HF to float
      */
 
      property float toFloat() { return shortToFloat(s); }
     alias toFloat this;
[...] This is cool, I've never thought of using alias this on a property function. I'll have to start using that in my code. :-) The rest of the code is tl;dr, but I think it does showcase quite well the power of user-defined types in D. Now we just have to iron out implicit conversion from literals (and I mean that in general, not just for this particular example), and it will just be great. T -- Which is worse: ignorance or apathy? Who knows? Who cares? -- Erich Schubert
Dec 13 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 12/13/2012 10:19 PM, H. S. Teoh wrote:
 This is cool, I've never thought of using alias this on a  property
 function. I'll have to start using that in my code. :-)
That's actually why I wrote it - as a "template" for how to do similar user defined types.
 The rest of the code is tl;dr, but I think it does showcase quite well
 the power of user-defined types in D. Now we just have to iron out
 implicit conversion from literals (and I mean that in general, not just
 for this particular example), and it will just be great.
Yup. Once all that works, I'd like to incorporate halffloat into Phobos.
Dec 14 2012
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Dec 14, 2012 at 12:39:04AM -0800, Walter Bright wrote:
 On 12/13/2012 10:19 PM, H. S. Teoh wrote:
This is cool, I've never thought of using alias this on a  property
function. I'll have to start using that in my code. :-)
That's actually why I wrote it - as a "template" for how to do similar user defined types.
[...] I'm so impressed by this trick that I decided to do a writeup of it on the wiki: http://wiki.dlang.org/Implicit_conversions_in_user_types T -- If Java had true garbage collection, most programs would delete themselves upon execution. -- Robert Sewell
Dec 14 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 12/14/2012 4:02 PM, H. S. Teoh wrote:
 I'm so impressed by this trick that I decided to do a writeup of it on
 the wiki:

 	http://wiki.dlang.org/Implicit_conversions_in_user_types
I'd prefer to call it a "technique" rather than a "trick", because this is what alias this was designed for!
Dec 14 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 15 December 2012 at 02:23:18 UTC, Walter Bright 
wrote:
 I'd prefer to call it a "technique" rather than a "trick", 
 because this is what alias this was designed for!
BTW is there any idea for the timetable of multiple alas this like described in Andrei's book?
Dec 14 2012
parent Walter Bright <newshound2 digitalmars.com> writes:
On 12/14/2012 6:27 PM, Adam D. Ruppe wrote:
 BTW is there any idea for the timetable of multiple alas this like described in
 Andrei's book?
Not at the moment.
Dec 14 2012