The OpenD Programming Language

1 // Written in the D programming language.
2 
3 /**
4 This module implements support for normalized integers.
5 
6 Authors:    Manu Evans
7 Copyright:  Copyright (c) 2015, Manu Evans.
8 License:    $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 Source:     $(PHOBOSSRC std/experimental/_normint.d)
10 */
11 module std.experimental.normint;
12 
13 import std.traits : isIntegral, isSigned, isUnsigned, isFloatingPoint, Unsigned;
14 
15 @safe pure nothrow @nogc:
16 
17 /**
18 Check if $(D_INLINECODE I) is a valid NormalizedInt type.
19 Valid integers are $(D_INLINECODE (u)byte), $(D_INLINECODE (u)short), $(D_INLINECODE (u)int). $(D_INLINECODE (u)long) is not supported.
20 */
21 enum isNormalizedIntegralType(I) = isIntegral!I && I.sizeof < 8;
22 
23 
24 /**
25 Normalized integers express a fractional range of values within an integer data type.
26 
27 Unsigned integers map the values $(D_INLINECODE [0, I.max]) to the fractional values $(D_INLINECODE [0.0, 1.0]) in equal increments.$(BR)
28 Signed integers represent the values $(D_INLINECODE [-I.max, I.max]) to fractional values $(D_INLINECODE [-1.0, 1.0]) in equal increments. $(D_INLINECODE I.min) is outside the nominal integer range and is clamped to represent $(D_INLINECODE -1.0).
29 
30 Params: $(D_INLINECODE I) = $(D_INLINECODE (u)byte), $(D_INLINECODE (u)short), $(D_INLINECODE (u)int). $(D_INLINECODE (u)long) is not supported.
31 */
32 struct NormalizedInt(I) if (isNormalizedIntegralType!I)
33 {
34 @safe:
35     static import std.algorithm.comparison;
36 
37     string toString() const
38     {
39         import std.conv : to;
40         return to!string(cast(double)this);
41     }
42 
43 pure nothrow @nogc:
44 
45     /** The actual value */
46     I value;
47 
48     /** Integral storage type. */
49     alias IntType = I;
50 
51     /** Maximum integral value. */
52     enum I max = I.max;
53     /** Minimum integral value. */
54     enum I min = isSigned!I ? -cast(int)I.max : 0;
55 
56     /** Maximum floating point value. */
57     enum max_float = 1.0;
58     /** Minimum floating point value. */
59     enum min_float = isSigned!I ? -1.0 : 0.0;
60 
61     /** Construct a $(D_INLINECODE NormalizedInt) from an integer representation. */
62     this(I value)
63     {
64         this.value = value;
65     }
66 
67     /** Construct a $(D_INLINECODE NormalizedInt) from a floating point representation. The value is clamped to the range $(D_INLINECODE [min, max]). */
68     this(F)(F value) if (isFloatingPoint!F)
69     {
70         this.value = floatToNormInt!I(value);
71     }
72 
73     /** Unary operators. */
74     NormalizedInt!I opUnary(string op)() const
75     {
76         static if (op == "-" && isUnsigned!I)
77             return NormalizedInt!I(0); // unsigned negate saturates at 0
78         else
79             return NormalizedInt!I(mixin("cast(I)" ~ op ~ "cast(WorkInt!I)value"));
80     }
81 
82     /** Binary operators. */
83     NormalizedInt!I opBinary(string op)(NormalizedInt!I rh) const if (op == "+" || op == "-")
84     {
85         auto r = mixin("cast(WorkInt!I)value " ~ op ~ " rh.value");
86         r = std.algorithm.comparison.min(r, max);
87         static if (op == "-")
88             r = std.algorithm.comparison.max(r, min);
89         return NormalizedInt!I(cast(I)r);
90     }
91 
92     /** Binary operators. */
93     NormalizedInt!I opBinary(string op)(NormalizedInt!I rh) const if (op == "*" || op == "^^")
94     {
95         static if (is(I == ubyte) && op == "*")
96         {
97             uint r = cast(uint)value * rh.value;
98             r = r * 0x1011 >> 20;
99             return NormalizedInt!I(cast(I)r);
100         }
101         else static if (is(I == ushort) && op == "*")
102         {
103             ulong r = cast(ulong)value * rh.value;
104             r = r * 0x10_0011 >> 36;
105             return NormalizedInt!I(cast(I)r);
106         }
107         // *** SLOW PATH ***
108         // do it with floats
109         else static if (op == "*")
110         {
111             // use a post-multiply divide; less muls!
112             double a = value;
113             double b = rh.value;
114             static if (isSigned!I)
115             {
116                 a = std.algorithm.comparison.max(a, cast(double)min);
117                 b = std.algorithm.comparison.max(b, cast(double)min);
118             }
119             double r = a * b * (1.0/max);
120             return NormalizedInt!I(cast(I)r);
121         }
122         else
123         {
124             // pow must be normalised first.
125             double a = value * (1.0/max);
126             double b = rh.value * (1.0/max);
127             static if (isSigned!I)
128             {
129                 a = std.algorithm.comparison.max(a, -1.0);
130                 b = std.algorithm.comparison.max(b, -1.0);
131             }
132             double r = a^^b * double(max);
133             if (isUnsigned!I || r >= 0)
134                 return NormalizedInt!I(cast(I)(r + 0.50));
135             else
136                 return NormalizedInt!I(cast(I)(r - 0.50));
137         }
138     }
139 
140     /** Binary operators. */
141     NormalizedInt!I opBinary(string op)(NormalizedInt!I rh) const if (op == "/" || op == "%")
142     {
143         return mixin("this " ~ op ~ " cast(FloatTypeFor!I)rh");
144     }
145 
146     /** Binary operators. */
147     NormalizedInt!I opBinary(string op, T)(T rh) const if (isNormalizedIntegralType!T && op == "*")
148     {
149         return NormalizedInt!I(cast(I)std.algorithm.comparison.clamp(cast(WorkInt!I)value * rh, min, max));
150     }
151 
152     /** Binary operators. */
153     NormalizedInt!I opBinary(string op, T)(T rh) const if (isNormalizedIntegralType!T && (op == "/" || op == "%"))
154     {
155         return NormalizedInt!I(cast(I)mixin("value " ~ op ~ " rh"));
156     }
157 
158     /** Binary operators. */
159     NormalizedInt!I opBinary(string op, F)(F rh) const if (isFloatingPoint!F && (op == "*" || op == "/" || op == "%" || op == "^^"))
160     {
161         return NormalizedInt!I(mixin("(cast(F)this) " ~ op ~ " rh"));
162     }
163 
164     /** Binary operators. */
165     NormalizedInt!I opBinary(string op)(NormalizedInt!I rh) const if (op == "|" || op == "&" || op == "^")
166     {
167         return NormalizedInt!I(cast(I)(mixin("value " ~ op ~ " rh.value")));
168     }
169 
170     /** Binary operators. */
171     NormalizedInt!I opBinary(string op)(int rh) const if (op == "|" || op == "&" || op == "^" || op == "<<" || op == ">>" || op == ">>>")
172     {
173         return NormalizedInt!I(cast(I)(mixin("value " ~ op ~ " rh")));
174     }
175 
176     /** Equality operators. */
177     bool opEquals(NormalizedInt!I rh) const
178     {
179         return value == rh.value;
180     }
181     /**
182     Integral equality operator.$(BR)
183     If $(D_INLINECODE rh) is outside of the integral range, $(D_INLINECODE rh) will not be clamped and comparison will return $(D_INLINECODE false).
184     */
185     bool opEquals(T)(T rh) const if (isNormalizedIntegralType!T)
186     {
187         return value == rh;
188     }
189     /**
190     Floating point equality operator.$(BR)
191     If $(D_INLINECODE rh) is outside of the nominal range, $(D_INLINECODE rh) will not be clamped and comparison will return $(D_INLINECODE false).
192     */
193     bool opEquals(F)(F rh) const if (isFloatingPoint!F)
194     {
195         return cast(F)this == rh;
196     }
197 
198     /** Comparison operators. */
199     int opCmp(NormalizedInt!I rh) const
200     {
201         return value - rh.value;
202     }
203     /** Comparison operators. */
204     int opCmp(T)(T rh) const if (isNormalizedIntegralType!T)
205     {
206         return value - rh;
207     }
208     /** Comparison operators. */
209     int opCmp(F)(F rh) const if (isFloatingPoint!F)
210     {
211         F f = cast(F)this;
212         return f < rh ? -1 : (f > rh ? 1 : 0);
213     }
214 
215     /** Binary assignment operators. */
216     ref NormalizedInt!I opOpAssign(string op, T)(T rh) if (is(T == NormalizedInt!I) || isFloatingPoint!T || isNormalizedIntegralType!T)
217     {
218         this = mixin("this " ~ op ~ "rh");
219         return this;
220     }
221 
222     /** Cast between $(D_INLINECODE NormalizedInt) types. */
223     NormInt opCast(NormInt)() const if (is(NormInt == NormalizedInt!T, T))
224     {
225         static if (is(NormInt == NormalizedInt!T, T))
226             return NormInt(convertNormInt!T(value));
227         else
228             static assert(false, "Shouldn't be possible!");
229     }
230 
231     /** Floating point cast operator. */
232     F opCast(F)() const if (isFloatingPoint!F)
233     {
234         return normIntToFloat!F(value);
235     }
236 }
237 ///
238 unittest
239 {
240     auto x = NormalizedInt!ubyte(200);
241     auto y = NormalizedInt!ubyte(50);
242 
243     auto z = x + y;                      assert(z == 250);                 // add as expected
244     z += y;                              assert(z == 255);                 // overflow saturates
245                                          assert(cast(float)z == 1.0);      // maximum value is floating point 1.0
246     z -= x;                              assert(z == 55);                  // subtract as expected
247     z -= x;                              assert(z == 0);                   // underflow saturates
248     z = y * 2;                           assert(z == 100);                 // multiply by integer
249     z = x * 0.5;                         assert(z == 100);                 // multiply by float
250     z *= 3;                              assert(z == 255);                 // multiply overflow saturates
251     z *= y;                              assert(z == 50);                  // multiply is performed in normalized space
252     z *= y;                              assert(z == 9);                   // multiplication rounds *down*
253     z /= 2;                              assert(z == 4);                   // division works as expected, rounds down
254     z /= y;                              assert(z == 20);                  // division is performed in normalized space
255     z /= y * y;                          assert(z == 255);                 // division overflow saturates
256     z = -z;                              assert(z == 0);                   // unsigned negation saturates at zero
257 
258     auto u = NormalizedInt!short(-1.0);
259     auto v = NormalizedInt!short(0.5);
260 
261     auto w = cast(NormalizedInt!short)x; assert(w == 25700);               // casting to higher precision extends bit-pattern to preserve uniformity
262     w = -w;                              assert(w == -25700);              // negation works as expected
263     w *= 2;                              assert(w == -32767 && w == -1.0); // overflow saturates
264     w *= -0.5;                           assert(w == 16384 && w > 0.5);    // 0.5 is not exactly representable (odd number of positive integers)
265     w = w^^0.0;                          assert(w == 1.0);                 // pow as expected
266 
267     // check floating poing comparisons
268     static assert(NormalizedInt!ubyte(0xFF) == 1.0);
269     static assert(NormalizedInt!ubyte(0x00) == 0.0);
270     static assert(NormalizedInt!ubyte(0x80) > 0.5);
271     static assert(NormalizedInt!ubyte(0x7F) < 0.5);
272 
273     static assert(NormalizedInt!byte(127) == 1.0);
274     static assert(NormalizedInt!byte(-127) == -1.0);
275     static assert(NormalizedInt!byte(-128) == -1.0);
276     static assert(NormalizedInt!byte(0x00) == 0.0);
277     static assert(NormalizedInt!byte(0x40) > 0.5);
278     static assert(NormalizedInt!byte(0x3F) < 0.5);
279 }
280 
281 
282 /** Convert values between normalized integer types. */
283 To convertNormInt(To, From)(From i) if (isIntegral!From && isIntegral!To)
284 {
285     return cast(To)convertNormBits!(From.sizeof*8, isSigned!From, To.sizeof*8, isSigned!To, Unsigned!To, Unsigned!From)(i);
286 }
287 ///
288 unittest
289 {
290     // unsigned -> unsigned
291     static assert(convertNormInt!ubyte(ushort(0x3765)) == 0x37);
292     static assert(convertNormInt!ushort(ubyte(0x37)) == 0x3737);
293     static assert(convertNormInt!uint(ubyte(0x35)) == 0x35353535);
294 
295     // signed -> unsigned
296     static assert(convertNormInt!ubyte(short(-61)) == 0);
297     static assert(convertNormInt!ubyte(short(0x3795)) == 0x6F);
298     static assert(convertNormInt!ushort(byte(0x37)) == 0x6EDD);
299     static assert(convertNormInt!uint(byte(0x35)) == 0x6AD5AB56);
300 
301     // unsigned -> signed
302     static assert(convertNormInt!byte(ushort(0x3765)) == 0x1B);
303     static assert(convertNormInt!short(ubyte(0x37)) == 0x1B9B);
304     static assert(convertNormInt!int(ubyte(0x35)) == 0x1A9A9A9A);
305 
306     // signed -> signed
307     static assert(convertNormInt!short(byte(-127)) == -32767);
308     static assert(convertNormInt!short(byte(-128)) == -32767);
309     static assert(convertNormInt!byte(short(0x3795)) == 0x37);
310     static assert(convertNormInt!byte(short(-28672)) == -112);
311     static assert(convertNormInt!short(byte(0x37)) == 0x376E);
312     static assert(convertNormInt!short(byte(-109)) == -28123);
313 }
314 
315 /** Convert a float to a normalized integer. */
316 To floatToNormInt(To, From)(From f) if (isFloatingPoint!From && isIntegral!To)
317 {
318     return cast(To)floatToNormBits!(To.sizeof*8, isSigned!To, Unsigned!To)(f);
319 }
320 ///
321 unittest
322 {
323     static assert(floatToNormInt!ubyte(0.5) == 0x80);
324 }
325 
326 /** Convert a normalized integer to a float. */
327 To normIntToFloat(To, From)(From i) if (isIntegral!From && isFloatingPoint!To)
328 {
329     return normBitsToFloat!(From.sizeof*8, isSigned!From, To)(cast(Unsigned!From)i);
330 }
331 ///
332 unittest
333 {
334     static assert(normIntToFloat!(double, ubyte)(0xFF) == 1.0);
335 }
336 
337 package:
338 
339 // try and use the preferred float type
340 // if the int type exceeds the preferred float precision, we'll upgrade the float
341 template FloatTypeFor(IntType, RequestedFloat = float)
342 {
343     static if (IntType.sizeof > 2)
344         alias FloatTypeFor = double;
345     else
346         alias FloatTypeFor = RequestedFloat;
347 }
348 
349 enum BitsUMax(size_t n) = (1L << n)-1;
350 enum BitsSMax(size_t n) = (1 << (n-1))-1;
351 enum SignBit(size_t n) = (1 << (n-1));
352 
353 //pragma(inline, true) // Error: cannot inline function
354 T floatToNormBits(size_t bits, bool signed, T = uint, F)(F f) pure nothrow @nogc @safe if (isUnsigned!T && isFloatingPoint!F)
355 {
356     static if(bits == 1)
357     {
358         static if (!signed)
359             return f >= 0.5 ? 1 : 0;
360         else
361             return f <= -0.5 ? 1 : 0;
362     }
363     else static if (!signed)
364     {
365         if(f >= 1)
366             return BitsUMax!bits;
367         else if(f <= 0)
368             return 0;
369         return cast(T)(f*BitsUMax!bits + 0.5);
370     }
371     else
372     {
373         if (f >= 0)
374         {
375             if(f >= 1)
376                 return BitsSMax!bits;
377             return cast(T)(f*BitsSMax!bits + 0.5);
378         }
379         if(f <= -1)
380             return -BitsSMax!bits & BitsUMax!bits;
381         return cast(T)(f*BitsSMax!bits - 0.5) & BitsUMax!bits;
382     }
383 }
384 unittest
385 {
386     // float unpacking
387     static assert(floatToNormBits!(1, false)(0.0) == 0);
388     static assert(floatToNormBits!(1, false)(0.3) == 0);
389     static assert(floatToNormBits!(1, false)(0.5) == 1); // round to nearest
390     static assert(floatToNormBits!(1, false)(1.0) == 1);
391     static assert(floatToNormBits!(1, false)(2.0) == 1);
392     static assert(floatToNormBits!(1, false)(-1.0) == 0);
393     static assert(floatToNormBits!(1, false)(-2.0) == 0);
394 
395     static assert(floatToNormBits!(2, false)(0.0) == 0);
396     static assert(floatToNormBits!(2, false)(0.3) == 1);
397     static assert(floatToNormBits!(2, false)(0.5) == 2);
398     static assert(floatToNormBits!(2, false)(1.0) == 3);
399     static assert(floatToNormBits!(2, false)(2.0) == 3);
400     static assert(floatToNormBits!(2, false)(-1.0) == 0);
401     static assert(floatToNormBits!(2, false)(-2.0) == 0);
402 
403     static assert(floatToNormBits!(6, false)(0.0) == 0);
404     static assert(floatToNormBits!(6, false)(0.3) == 19);
405     static assert(floatToNormBits!(6, false)(0.5) == 32);
406     static assert(floatToNormBits!(6, false)(1.0) == 63);
407     static assert(floatToNormBits!(6, false)(2.0) == 63);
408     static assert(floatToNormBits!(6, false)(-1.0) == 0);
409     static assert(floatToNormBits!(6, false)(-2.0) == 0);
410 
411     static assert(floatToNormBits!(9, true)(0.0) == 0x00);
412     static assert(floatToNormBits!(9, true)(0.3) == 0x4D);
413     static assert(floatToNormBits!(9, true)(0.5) == 0x80);
414     static assert(floatToNormBits!(9, true)(1.0) == 0xFF);
415     static assert(floatToNormBits!(9, true)(2.0) == 0xFF);
416     static assert(floatToNormBits!(9, true)(-0.5) == 0x180);
417     static assert(floatToNormBits!(9, true)(-1.0) == 0x101);
418     static assert(floatToNormBits!(9, true)(-2.0) == 0x101);
419 }
420 
421 pragma(inline, true)
422 F normBitsToFloat(size_t bits, bool signed, F = float)(uint v) pure nothrow @nogc @safe if (isFloatingPoint!F)
423 {
424     static if (!signed)
425         return v / F(BitsUMax!bits);
426     else static if (bits == 1)
427         return v ? F(-1.0) : F(0.0);
428     else
429     {
430         import std.algorithm.comparison : max;
431         return max((cast(int)(v << (32 - bits)) >> (32 - bits)) / F(BitsSMax!bits), F(-1));
432     }
433 }
434 unittest
435 {
436     // float unpacking
437     static assert(normBitsToFloat!(1, false, float)(0) == 0);
438     static assert(normBitsToFloat!(1, false, float)(1) == 1);
439     static assert(normBitsToFloat!(1, true, float)(0) == 0);
440     static assert(normBitsToFloat!(1, true, float)(1) == -1);
441 
442     static assert(normBitsToFloat!(2, true, float)(0) == 0);
443     static assert(normBitsToFloat!(2, true, float)(1) == 1);
444     static assert(normBitsToFloat!(2, true, float)(2) == -1);
445     static assert(normBitsToFloat!(2, true, float)(3) == -1);
446 
447     static assert(normBitsToFloat!(3, true, float)(0) == 0);
448     static assert(normBitsToFloat!(3, true, float)(1) == 1.0/3);
449     static assert(normBitsToFloat!(3, true, float)(2) == 2.0/3);
450     static assert(normBitsToFloat!(3, true, float)(3) == 1);
451     static assert(normBitsToFloat!(3, true, float)(4) == -1);
452     static assert(normBitsToFloat!(3, true, float)(5) == -1);
453     static assert(normBitsToFloat!(3, true, float)(6) == -2.0/3);
454     static assert(normBitsToFloat!(3, true, float)(7) == -1.0/3);
455 }
456 
457 pragma(inline, true)
458 T convertNormBits(size_t srcBits, bool srcSigned, size_t destBits, bool destSigned, T = uint, S)(S v) pure nothrow @nogc @safe if (isUnsigned!T && isUnsigned!S)
459 {
460     // TODO: this should be tested for performance.
461     //       we can optimise the small->large conversions with table lookups?
462 
463     // hack for 1-bit src
464     static if (srcBits == 1)
465     {
466         static if (!srcSigned && !destSigned)
467             return v ? BitsUMax!destBits : 0;
468         else static if (!srcSigned && destSigned)
469             return v ? BitsSMax!destBits : 0;
470         else static if (srcSigned && !destSigned)
471             return 0; // always clamp to zero
472         else static if (srcSigned && destSigned)
473             return v ? SignBit!destBits : 0;
474     }
475     else static if (!destSigned)
476     {
477         static if (!srcSigned)
478         {
479             static if (destBits > srcBits)
480             {
481                 // up conversion is tricky
482                 template BitRepeat(size_t srcWidth, size_t destWidth)
483                 {
484                     template Impl(size_t i)
485                     {
486                         static if (i < srcWidth)
487                             enum Impl = 1 << i;
488                         else
489                             enum Impl = (1 << i) | Impl!(i - srcWidth);
490                     }
491                     enum BitRepeat = Impl!(destWidth - srcWidth);
492                 }
493 
494                 enum reps = destBits / srcBits;
495                 static if (reps <= 1)
496                     T r = cast(T)(v << (destBits - srcBits));
497                 else static if (reps == 2) // TODO: benchmark if imul is faster for reps == 2...
498                     T r = cast(T)((v << (destBits - srcBits)) | (v << (destBits - srcBits*2)));
499                 else static if (reps > 2)
500                     T r = cast(T)(v * BitRepeat!(srcBits, destBits));
501                 static if (destBits%srcBits != 0)
502                     r |= cast(T)(v >> (srcBits - destBits%srcBits));
503                 return r;
504             }
505             else
506                 return cast(T)(v >> (srcBits - destBits));
507         }
508         else
509         {
510             // signed -> unsigned
511             if (v & SignBit!srcBits) // if src is negative, clamp to 0
512                 return 0;
513             else
514                 return convertNormBits!(srcBits - 1, false, destBits, destSigned, T, S)(v);
515         }
516     }
517     else
518     {
519         static if (!srcSigned)
520         {
521             // unsigned -> signed
522             return convertNormBits!(srcBits, false, destBits - 1, false, T, S)(v);
523         }
524         else
525         {
526             if (v & SignBit!srcBits)
527                 return cast(T)-cast(WorkInt!T)convertNormBits!(srcBits - 1, false, destBits - 1, false, T, S)(((v ^ SignBit!srcBits) == 0 ? cast(S)~cast(WorkInt!S)v : cast(S)-cast(WorkInt!S)v) & BitsSMax!srcBits) & BitsUMax!destBits;
528             else
529                 return convertNormBits!(srcBits - 1, false, destBits - 1, false, T, S)(v);
530         }
531     }
532 }
533 unittest
534 {
535     // unsigned -> unsigned int
536     static assert(convertNormBits!(1, false, 8, false, ubyte)(0u) == 0x00);
537     static assert(convertNormBits!(1, false, 8, false, ubyte)(1u) == 0xFF);
538     static assert(convertNormBits!(1, false, 30, false, uint)(1u) == 0x3FFFFFFF);
539 
540     static assert(convertNormBits!(2, false, 8, false, ubyte)(0u) == 0x00);
541     static assert(convertNormBits!(2, false, 8, false, ubyte)(1u) == 0x55);
542     static assert(convertNormBits!(2, false, 4, false, ubyte)(2u) == 0x0A);
543     static assert(convertNormBits!(2, false, 8, false, ubyte)(3u) == 0xFF);
544     static assert(convertNormBits!(2, false, 28, false, uint)(2u) == 0x0AAAAAAA);
545     static assert(convertNormBits!(2, false, 32, false, uint)(3u) == 0xFFFFFFFF);
546 
547     static assert(convertNormBits!(3, false, 8, false, ubyte)(0u) == 0x00); // 0b00000000
548     static assert(convertNormBits!(3, false, 8, false, ubyte)(1u) == 0x24); // 0b00100100
549     static assert(convertNormBits!(3, false, 8, false, ubyte)(2u) == 0x49); // 0b01001001
550     static assert(convertNormBits!(3, false, 8, false, ubyte)(3u) == 0x6D); // 0b01101101
551     static assert(convertNormBits!(3, false, 8, false, ubyte)(4u) == 0x92); // 0b10010010
552     static assert(convertNormBits!(3, false, 8, false, ubyte)(5u) == 0xB6); // 0b10110110
553     static assert(convertNormBits!(3, false, 8, false, ubyte)(6u) == 0xDB); // 0b11011011
554     static assert(convertNormBits!(3, false, 8, false, ubyte)(7u) == 0xFF); // 0b11111111
555     static assert(convertNormBits!(3, false, 32, false, uint)(4u) == 0x92492492);
556     static assert(convertNormBits!(3, false, 32, false, uint)(7u) == 0xFFFFFFFF);
557 
558     // unsigned -> signed int
559     static assert(convertNormBits!(1, false, 8, true, ubyte)(0u) == 0x00);
560     static assert(convertNormBits!(1, false, 8, true, ubyte)(1u) == 0x7F);
561     static assert(convertNormBits!(1, false, 32, true, uint)(1u) == 0x7FFFFFFF);
562 
563     static assert(convertNormBits!(2, false, 8, true, ubyte)(0u) == 0x00);
564     static assert(convertNormBits!(2, false, 8, true, ubyte)(1u) == 0x2A);
565     static assert(convertNormBits!(2, false, 8, true, ubyte)(2u) == 0x55);
566     static assert(convertNormBits!(2, false, 8, true, ubyte)(3u) == 0x7F);
567     static assert(convertNormBits!(2, false, 32, true, uint)(2u) == 0x55555555);
568     static assert(convertNormBits!(2, false, 32, true, uint)(3u) == 0x7FFFFFFF);
569 
570     static assert(convertNormBits!(3, false, 8, true, ubyte)(0u) == 0x00); // 0b00000000
571     static assert(convertNormBits!(3, false, 8, true, ubyte)(1u) == 0x12); // 0b00010010
572     static assert(convertNormBits!(3, false, 8, true, ubyte)(2u) == 0x24); // 0b00100100
573     static assert(convertNormBits!(3, false, 8, true, ubyte)(3u) == 0x36); // 0b00110110
574     static assert(convertNormBits!(3, false, 8, true, ubyte)(4u) == 0x49); // 0b01001001
575     static assert(convertNormBits!(3, false, 8, true, ubyte)(5u) == 0x5B); // 0b01011011
576     static assert(convertNormBits!(3, false, 8, true, ubyte)(6u) == 0x6D); // 0b01101101
577     static assert(convertNormBits!(3, false, 8, true, ubyte)(7u) == 0x7F); // 0b01111111
578     static assert(convertNormBits!(3, false, 32, true, uint)(4u) == 0x49249249);
579     static assert(convertNormBits!(3, false, 32, true, uint)(7u) == 0x7FFFFFFF);
580 
581     // signed -> unsigned int
582     static assert(convertNormBits!(1, true, 8, false, ubyte)(0u) == 0x00);
583     static assert(convertNormBits!(1, true, 8, false, ubyte)(1u) == 0x00);
584     static assert(convertNormBits!(1, true, 32, false, uint)(1u) == 0x00000000);
585 
586     static assert(convertNormBits!(2, true, 8, false, ubyte)(0u) == 0x00);
587     static assert(convertNormBits!(2, true, 8, false, ubyte)(1u) == 0xFF);
588     static assert(convertNormBits!(2, true, 8, false, ubyte)(2u) == 0x0);
589     static assert(convertNormBits!(2, true, 8, false, ubyte)(3u) == 0x0);
590     static assert(convertNormBits!(2, true, 32, false, uint)(1u) == 0xFFFFFFFF);
591     static assert(convertNormBits!(2, true, 32, false, uint)(3u) == 0x00000000);
592 
593     static assert(convertNormBits!(3, true, 8, false, ubyte)(0u) == 0x00); // 0b00000000
594     static assert(convertNormBits!(3, true, 8, false, ubyte)(1u) == 0x55); // 0b01010101
595     static assert(convertNormBits!(3, true, 8, false, ubyte)(2u) == 0xAA); // 0b10101010
596     static assert(convertNormBits!(3, true, 8, false, ubyte)(3u) == 0xFF); // 0b11111111
597     static assert(convertNormBits!(3, true, 8, false, ubyte)(4u) == 0x00); // 0b00000000
598     static assert(convertNormBits!(3, true, 8, false, ubyte)(5u) == 0x00); // 0b00000000
599     static assert(convertNormBits!(3, true, 8, false, ubyte)(6u) == 0x00); // 0b00000000
600     static assert(convertNormBits!(3, true, 8, false, ubyte)(7u) == 0x00); // 0b00000000
601     static assert(convertNormBits!(3, true, 32, false, uint)(2u) == 0xAAAAAAAA);
602     static assert(convertNormBits!(3, true, 32, false, uint)(7u) == 0x00000000);
603 
604     // signed -> signed int
605     static assert(convertNormBits!(1, true, 8, true, ubyte)(0u) == 0x00);
606     static assert(convertNormBits!(1, true, 8, true, ubyte)(1u) == 0x80);
607     static assert(convertNormBits!(1, true, 32, true, uint)(1u) == 0x80000000);
608 
609     static assert(convertNormBits!(2, true, 8, true, ubyte)(0u) == 0x00);
610     static assert(convertNormBits!(2, true, 8, true, ubyte)(1u) == 0x7F);
611     static assert(convertNormBits!(2, true, 8, true, ubyte)(2u) == 0x81);
612     static assert(convertNormBits!(2, true, 8, true, ubyte)(3u) == 0x81);
613     static assert(convertNormBits!(2, true, 32, true, uint)(1u) == 0x7FFFFFFF);
614     static assert(convertNormBits!(2, true, 32, true, uint)(3u) == 0x80000001);
615 
616     static assert(convertNormBits!(3, true, 8, true, ubyte)(0u) == 0x00);
617     static assert(convertNormBits!(3, true, 8, true, ubyte)(1u) == 0x2A);
618     static assert(convertNormBits!(3, true, 8, true, ubyte)(2u) == 0x55);
619     static assert(convertNormBits!(3, true, 8, true, ubyte)(3u) == 0x7F);
620     static assert(convertNormBits!(3, true, 8, true, ubyte)(4u) == 0x81);
621     static assert(convertNormBits!(3, true, 8, true, ubyte)(5u) == 0x81);
622     static assert(convertNormBits!(3, true, 8, true, ubyte)(6u) == 0xAB);
623     static assert(convertNormBits!(3, true, 8, true, ubyte)(7u) == 0xD6);
624     static assert(convertNormBits!(3, true, 32, true, uint)(2u) == 0x55555555);
625     static assert(convertNormBits!(3, true, 32, true, uint)(4u) == 0x80000001);
626     static assert(convertNormBits!(3, true, 32, true, uint)(5u) == 0x80000001);
627     static assert(convertNormBits!(3, true, 32, true, uint)(7u) == 0xD5555556);
628 }
629 
630 
631 private:
632 
633 template WorkInt(I)
634 {
635     static if (I.sizeof < 4)
636         alias WorkInt = int;   // normal integer promotion for small int's
637     else static if (is(I == ulong))
638         alias WorkInt = ulong; // ulong has no larger signed type
639     else
640         alias WorkInt = long;  // (u)int promotes to long, so we can overflow and saturate instead of wrapping
641 }
642 unittest
643 {
644     static assert(is(WorkInt!short == int));
645     static assert(is(WorkInt!ubyte == int));
646     static assert(is(WorkInt!uint == long));
647     static assert(is(WorkInt!long == long));
648     static assert(is(WorkInt!ulong == ulong));
649 }
650 
651 // all tests
652 unittest
653 {
654     // construction
655     static assert(NormalizedInt!ubyte(100) == 100);
656 
657     static assert(NormalizedInt!ubyte(1.0) == 255);
658     static assert(NormalizedInt!ubyte(2.0) == 255);
659     static assert(NormalizedInt!ubyte(0.5) == 128);
660     static assert(NormalizedInt!ubyte(-1.0) == 0);
661     static assert(NormalizedInt!byte(-1.0) == -127);
662     static assert(NormalizedInt!byte(-2.0) == -127);
663 
664     // unary operators
665     static assert(+NormalizedInt!ubyte(0.5) == 128);
666     static assert(-NormalizedInt!ubyte(0.5) == 0);
667     static assert(~NormalizedInt!ubyte(0.5) == 127);
668     static assert(+NormalizedInt!byte(0.5) == 64);
669     static assert(-NormalizedInt!byte(0.5) == -64);
670     static assert(~NormalizedInt!byte(0.5) == -65);
671 
672     // binary operators
673     static assert(NormalizedInt!ubyte(1.0) + NormalizedInt!ubyte(0.5) == 255);
674     static assert(NormalizedInt!ubyte(1.0) - NormalizedInt!ubyte(0.5) == 127);
675     static assert(NormalizedInt!ubyte(0.5) - NormalizedInt!ubyte(1.0) == 0);
676     static assert(NormalizedInt!byte(1.0) + NormalizedInt!byte(0.5) == 127);
677     static assert(NormalizedInt!byte(1.0) - NormalizedInt!byte(0.5) == 63);
678     static assert(NormalizedInt!byte(-0.5) - NormalizedInt!byte(1.0) == -127);
679 
680     // ubyte (has a fast no-float path)
681     static assert(NormalizedInt!ubyte(0xFF) * NormalizedInt!ubyte(0xFF) == 255);
682     static assert(NormalizedInt!ubyte(0xFF) * NormalizedInt!ubyte(0xFE) == 254);
683     static assert(NormalizedInt!ubyte(0xFF) * NormalizedInt!ubyte(0x80) == 128);
684     static assert(NormalizedInt!ubyte(0xFF) * NormalizedInt!ubyte(0x40) == 64);
685     static assert(NormalizedInt!ubyte(0xFF) * NormalizedInt!ubyte(0x02) == 2);
686     static assert(NormalizedInt!ubyte(0xFF) * NormalizedInt!ubyte(0x01) == 1);
687     static assert(NormalizedInt!ubyte(0x80) * NormalizedInt!ubyte(0xFF) == 128);
688     static assert(NormalizedInt!ubyte(0x80) * NormalizedInt!ubyte(0xFE) == 127);
689     static assert(NormalizedInt!ubyte(0x80) * NormalizedInt!ubyte(0x80) == 64);
690     static assert(NormalizedInt!ubyte(0x80) * NormalizedInt!ubyte(0x40) == 32);
691     static assert(NormalizedInt!ubyte(0x80) * NormalizedInt!ubyte(0x02) == 1);
692     static assert(NormalizedInt!ubyte(0x80) * NormalizedInt!ubyte(0x01) == 0);
693     static assert(NormalizedInt!ubyte(0x40) * NormalizedInt!ubyte(0xFF) == 64);
694     static assert(NormalizedInt!ubyte(0x40) * NormalizedInt!ubyte(0xFE) == 63);
695     static assert(NormalizedInt!ubyte(0x40) * NormalizedInt!ubyte(0x80) == 32);
696     static assert(NormalizedInt!ubyte(0x40) * NormalizedInt!ubyte(0x40) == 16);
697     static assert(NormalizedInt!ubyte(0x40) * NormalizedInt!ubyte(0x02) == 0);
698     static assert(NormalizedInt!ubyte(0x40) * NormalizedInt!ubyte(0x01) == 0);
699 
700     // positive byte
701     static assert(NormalizedInt!byte(cast(byte)0x7F) * NormalizedInt!byte(cast(byte)0x7F) == 127);
702     static assert(NormalizedInt!byte(cast(byte)0x7F) * NormalizedInt!byte(cast(byte)0x7E) == 126);
703     static assert(NormalizedInt!byte(cast(byte)0x7F) * NormalizedInt!byte(cast(byte)0x40) == 64);
704     static assert(NormalizedInt!byte(cast(byte)0x7F) * NormalizedInt!byte(cast(byte)0x02) == 2);
705     static assert(NormalizedInt!byte(cast(byte)0x7F) * NormalizedInt!byte(cast(byte)0x01) == 1);
706     static assert(NormalizedInt!byte(cast(byte)0x40) * NormalizedInt!byte(cast(byte)0x7F) == 64);
707     static assert(NormalizedInt!byte(cast(byte)0x40) * NormalizedInt!byte(cast(byte)0x7E) == 63);
708     static assert(NormalizedInt!byte(cast(byte)0x40) * NormalizedInt!byte(cast(byte)0x40) == 32);
709     static assert(NormalizedInt!byte(cast(byte)0x40) * NormalizedInt!byte(cast(byte)0x02) == 1);
710     static assert(NormalizedInt!byte(cast(byte)0x40) * NormalizedInt!byte(cast(byte)0x01) == 0);
711     static assert(NormalizedInt!byte(cast(byte)0x20) * NormalizedInt!byte(cast(byte)0x7F) == 32);
712     static assert(NormalizedInt!byte(cast(byte)0x20) * NormalizedInt!byte(cast(byte)0x7E) == 31);
713     static assert(NormalizedInt!byte(cast(byte)0x20) * NormalizedInt!byte(cast(byte)0x40) == 16);
714     static assert(NormalizedInt!byte(cast(byte)0x20) * NormalizedInt!byte(cast(byte)0x02) == 0);
715     static assert(NormalizedInt!byte(cast(byte)0x20) * NormalizedInt!byte(cast(byte)0x01) == 0);
716     // negative byte
717     static assert(NormalizedInt!byte(cast(byte)0x81) * NormalizedInt!byte(cast(byte)0x7F) == -127);
718     static assert(NormalizedInt!byte(cast(byte)0x81) * NormalizedInt!byte(cast(byte)0x7E) == -126);
719     static assert(NormalizedInt!byte(cast(byte)0x81) * NormalizedInt!byte(cast(byte)0x40) == -64);
720     static assert(NormalizedInt!byte(cast(byte)0x81) * NormalizedInt!byte(cast(byte)0x02) == -2);
721     static assert(NormalizedInt!byte(cast(byte)0x81) * NormalizedInt!byte(cast(byte)0x01) == -1);
722     static assert(NormalizedInt!byte(cast(byte)0xC0) * NormalizedInt!byte(cast(byte)0x7F) == -64);
723     static assert(NormalizedInt!byte(cast(byte)0xC0) * NormalizedInt!byte(cast(byte)0x7E) == -63);
724     static assert(NormalizedInt!byte(cast(byte)0xC0) * NormalizedInt!byte(cast(byte)0x40) == -32);
725     static assert(NormalizedInt!byte(cast(byte)0xC0) * NormalizedInt!byte(cast(byte)0x02) == -1);
726     static assert(NormalizedInt!byte(cast(byte)0xC0) * NormalizedInt!byte(cast(byte)0x01) == 0);
727     static assert(NormalizedInt!byte(cast(byte)0xE0) * NormalizedInt!byte(cast(byte)0x7F) == -32);
728     static assert(NormalizedInt!byte(cast(byte)0xE0) * NormalizedInt!byte(cast(byte)0x7E) == -31);
729     static assert(NormalizedInt!byte(cast(byte)0xE0) * NormalizedInt!byte(cast(byte)0x40) == -16);
730     static assert(NormalizedInt!byte(cast(byte)0xE0) * NormalizedInt!byte(cast(byte)0x02) == 0);
731     static assert(NormalizedInt!byte(cast(byte)0xE0) * NormalizedInt!byte(cast(byte)0x01) == 0);
732 
733     // ushort (has a fast no-float path)
734     static assert(NormalizedInt!ushort(0xFFFF) * NormalizedInt!ushort(0xFFFF) == 0xFFFF);
735     static assert(NormalizedInt!ushort(0xFFFF) * NormalizedInt!ushort(0xFFFE) == 0xFFFE);
736     static assert(NormalizedInt!ushort(0xFFFF) * NormalizedInt!ushort(0x8000) == 0x8000);
737     static assert(NormalizedInt!ushort(0xFFFF) * NormalizedInt!ushort(0x4000) == 0x4000);
738     static assert(NormalizedInt!ushort(0xFFFF) * NormalizedInt!ushort(0x0002) == 0x0002);
739     static assert(NormalizedInt!ushort(0xFFFF) * NormalizedInt!ushort(0x0001) == 0x0001);
740     static assert(NormalizedInt!ushort(0x8000) * NormalizedInt!ushort(0xFFFF) == 0x8000);
741     static assert(NormalizedInt!ushort(0x8000) * NormalizedInt!ushort(0xFFFE) == 0x7FFF);
742     static assert(NormalizedInt!ushort(0x8000) * NormalizedInt!ushort(0x8000) == 0x4000);
743     static assert(NormalizedInt!ushort(0x8000) * NormalizedInt!ushort(0x4000) == 0x2000);
744     static assert(NormalizedInt!ushort(0x8000) * NormalizedInt!ushort(0x0002) == 0x0001);
745     static assert(NormalizedInt!ushort(0x8000) * NormalizedInt!ushort(0x0001) == 0x0000);
746     static assert(NormalizedInt!ushort(0x4000) * NormalizedInt!ushort(0xFFFF) == 0x4000);
747     static assert(NormalizedInt!ushort(0x4000) * NormalizedInt!ushort(0xFFFE) == 0x3FFF);
748     static assert(NormalizedInt!ushort(0x4000) * NormalizedInt!ushort(0x8000) == 0x2000);
749     static assert(NormalizedInt!ushort(0x4000) * NormalizedInt!ushort(0x4000) == 0x1000);
750     static assert(NormalizedInt!ushort(0x4000) * NormalizedInt!ushort(0x0002) == 0x0000);
751     static assert(NormalizedInt!ushort(0x4000) * NormalizedInt!ushort(0x0001) == 0x0000);
752 
753     // uint
754     static assert(NormalizedInt!uint(0xFFFFFFFF) * NormalizedInt!uint(0xFFFFFFFF) == 0xFFFFFFFF);
755     static assert(NormalizedInt!uint(0xFFFFFFFF) * NormalizedInt!uint(0xFFFFFFFE) == 0xFFFFFFFE);
756     static assert(NormalizedInt!uint(0xFFFFFFFF) * NormalizedInt!uint(0x80000000) == 0x80000000);
757     static assert(NormalizedInt!uint(0xFFFFFFFF) * NormalizedInt!uint(0x40000000) == 0x40000000);
758     static assert(NormalizedInt!uint(0xFFFFFFFF) * NormalizedInt!uint(0x00000002) == 0x00000002);
759     static assert(NormalizedInt!uint(0xFFFFFFFF) * NormalizedInt!uint(0x00000001) == 0x00000001);
760     static assert(NormalizedInt!uint(0x80000000) * NormalizedInt!uint(0xFFFFFFFF) == 0x80000000);
761     static assert(NormalizedInt!uint(0x80000000) * NormalizedInt!uint(0xFFFFFFFE) == 0x7FFFFFFF);
762     static assert(NormalizedInt!uint(0x80000000) * NormalizedInt!uint(0x80000000) == 0x40000000);
763     static assert(NormalizedInt!uint(0x80000000) * NormalizedInt!uint(0x40000000) == 0x20000000);
764     static assert(NormalizedInt!uint(0x80000000) * NormalizedInt!uint(0x00000002) == 0x00000001);
765     static assert(NormalizedInt!uint(0x80000000) * NormalizedInt!uint(0x00000001) == 0x00000000);
766     static assert(NormalizedInt!uint(0x40000000) * NormalizedInt!uint(0xFFFFFFFF) == 0x40000000);
767     static assert(NormalizedInt!uint(0x40000000) * NormalizedInt!uint(0xFFFFFFFE) == 0x3FFFFFFF);
768     static assert(NormalizedInt!uint(0x40000000) * NormalizedInt!uint(0x80000000) == 0x20000000);
769     static assert(NormalizedInt!uint(0x40000000) * NormalizedInt!uint(0x40000000) == 0x10000000);
770     static assert(NormalizedInt!uint(0x40000000) * NormalizedInt!uint(0x00000002) == 0x00000000);
771     static assert(NormalizedInt!uint(0x40000000) * NormalizedInt!uint(0x00000001) == 0x00000000);
772 
773     // int
774     static assert(NormalizedInt!int(0x80000001) * NormalizedInt!int(0x7FFFFFFF) == 0x80000001);
775     static assert(NormalizedInt!int(0x80000001) * NormalizedInt!int(0x7FFFFFFE) == 0x80000002);
776     static assert(NormalizedInt!int(0x80000001) * NormalizedInt!int(0x40000000) == 0xC0000000);
777     static assert(NormalizedInt!int(0x80000001) * NormalizedInt!int(0x00000002) == 0xFFFFFFFE);
778     static assert(NormalizedInt!int(0x80000001) * NormalizedInt!int(0x00000001) == 0xFFFFFFFF);
779     static assert(NormalizedInt!int(0xC0000000) * NormalizedInt!int(0x7FFFFFFF) == 0xC0000000);
780     static assert(NormalizedInt!int(0xC0000000) * NormalizedInt!int(0x7FFFFFFE) == 0xC0000001);
781     static assert(NormalizedInt!int(0xC0000000) * NormalizedInt!int(0x40000000) == 0xE0000000);
782     static assert(NormalizedInt!int(0xC0000000) * NormalizedInt!int(0x00000002) == 0xFFFFFFFF);
783     static assert(NormalizedInt!int(0xC0000000) * NormalizedInt!int(0x00000001) == 0x00000000);
784     static assert(NormalizedInt!int(0xE0000000) * NormalizedInt!int(0x7FFFFFFF) == 0xE0000000);
785     static assert(NormalizedInt!int(0xE0000000) * NormalizedInt!int(0x7FFFFFFE) == 0xE0000001);
786     static assert(NormalizedInt!int(0xE0000000) * NormalizedInt!int(0x40000000) == 0xF0000000);
787     static assert(NormalizedInt!int(0xE0000000) * NormalizedInt!int(0x00000002) == 0x00000000);
788     static assert(NormalizedInt!int(0xE0000000) * NormalizedInt!int(0x00000001) == 0x00000000);
789 
790     static assert(NormalizedInt!ubyte(0x80) / NormalizedInt!ubyte(0xFF) == 0x80);
791     static assert(NormalizedInt!ubyte(0x80) / NormalizedInt!ubyte(0x80) == 0xFF);
792 
793     static assert(NormalizedInt!ubyte(0x80) % NormalizedInt!ubyte(0xFF) == 0x80);
794     static assert(NormalizedInt!ubyte(0x80) % NormalizedInt!ubyte(0x80) == 0);
795 
796     // binary vs int
797     static assert(NormalizedInt!ubyte(0x40) * 2 == 0x80);
798     static assert(NormalizedInt!ubyte(0x80) * 2 == 0xFF);
799     static assert(NormalizedInt!ubyte(0xFF) * 2 == 0xFF);
800     static assert(NormalizedInt!byte(32) * 2 == 64);
801     static assert(NormalizedInt!byte(64) * 2 == 127);
802     static assert(NormalizedInt!byte(127) * 2 == 127);
803     static assert(NormalizedInt!byte(-32) * 2 == -64);
804     static assert(NormalizedInt!byte(-64) * 2 == -127);
805     static assert(NormalizedInt!byte(-127) * 2 == -127);
806     static assert(NormalizedInt!byte(-32) * -2 == 64);
807     static assert(NormalizedInt!byte(-64) * -2 == 127);
808     static assert(NormalizedInt!uint(0xFFFFFFFF) * 2 == 0xFFFFFFFF);
809     static assert(NormalizedInt!int(0x7FFFFFFF) * -2 == 0x80000001);
810 
811     static assert(NormalizedInt!ubyte(0x40) / 2 == 0x20);
812     static assert(NormalizedInt!ubyte(0xFF) / 2 == 0x7F);
813 
814     static assert(NormalizedInt!ubyte(0x40) % 2 == 0);
815     static assert(NormalizedInt!ubyte(0xFF) % 2 == 1);
816 
817     // binary vs float
818     static assert(NormalizedInt!ubyte(0x40) * 2.0 == 0x80);
819     static assert(NormalizedInt!ubyte(0x80) * 2.0 == 0xFF);
820     static assert(NormalizedInt!ubyte(0xFF) * 2.0 == 0xFF);
821     static assert(NormalizedInt!byte(32) * 2.0 == 64);
822     static assert(NormalizedInt!byte(64) * 2.0 == 127);
823     static assert(NormalizedInt!byte(127) * 2.0 == 127);
824     static assert(NormalizedInt!byte(-32) * 2.0 == -64);
825     static assert(NormalizedInt!byte(-64) * 2.0 == -127);
826     static assert(NormalizedInt!byte(-127) * 2.0 == -127);
827     static assert(NormalizedInt!byte(-32) * -2.0 == 64);
828     static assert(NormalizedInt!byte(-64) * -2.0 == 127);
829     static assert(NormalizedInt!int(0x7FFFFFFF) * -2.0 == 0x80000001);
830 
831     static assert(NormalizedInt!ubyte(0x40) * 0.5 == 0x20);
832     static assert(NormalizedInt!ubyte(0x80) * 0.5 == 0x40);
833     static assert(NormalizedInt!ubyte(0xFF) * 0.5 == 0x80);
834     static assert(NormalizedInt!byte(32) * 0.5 == 16);
835     static assert(NormalizedInt!byte(64) * 0.5 == 32);
836     static assert(NormalizedInt!byte(127) * 0.5 == 64);
837     static assert(NormalizedInt!byte(-32) * 0.5 == -16);
838     static assert(NormalizedInt!byte(-64) * 0.5 == -32);
839     static assert(NormalizedInt!byte(-127) * 0.5 == -64);
840     static assert(NormalizedInt!byte(-32) * -0.5 == 16);
841     static assert(NormalizedInt!byte(-64) * -0.5 == 32);
842 
843     static assert(NormalizedInt!ubyte(0xFF) / 2.0 == 0x80);
844     static assert(NormalizedInt!ubyte(0xFF) % 0.5 == 0);
845 
846     // bitwise operators
847     static assert((NormalizedInt!uint(0x80) | NormalizedInt!uint(0x08)) == 0x88);
848     static assert((NormalizedInt!uint(0xF0) & NormalizedInt!uint(0x81)) == 0x80);
849     static assert((NormalizedInt!uint(0x81) ^ NormalizedInt!uint(0x80)) == 0x01);
850 
851     static assert(NormalizedInt!uint(0x08000000) << 2 == 0x20000000);
852     static assert(NormalizedInt!int(0x80000000) >> 7 == 0xFF000000);
853     static assert(NormalizedInt!int(0x80000000) >>> 7 == 0x01000000);
854 
855     // casts
856     // up cast
857     static assert(cast(NormalizedInt!ushort)NormalizedInt!ubyte(0xFF) == 0xFFFF);
858     static assert(cast(NormalizedInt!ushort)NormalizedInt!ubyte(0x81) == 0x8181);
859 
860     // down cast
861     static assert(cast(NormalizedInt!ubyte)NormalizedInt!ushort(0xFFFF) == 0xFF);
862     static assert(cast(NormalizedInt!ubyte)NormalizedInt!ushort(0x9F37) == 0x9F);
863 
864     // signed -> unsigned
865     static assert(cast(NormalizedInt!ubyte)NormalizedInt!byte(127) == 0xFF);
866     static assert(cast(NormalizedInt!ushort)NormalizedInt!byte(127) == 0xFFFF);
867     static assert(cast(NormalizedInt!ubyte)NormalizedInt!byte(-127) == 0);
868     static assert(cast(NormalizedInt!ubyte)NormalizedInt!byte(-128) == 0);
869     static assert(cast(NormalizedInt!ushort)NormalizedInt!byte(-127) == 0);
870     static assert(cast(NormalizedInt!ubyte)NormalizedInt!short(-32767) == 0);
871     static assert(cast(NormalizedInt!ubyte)NormalizedInt!short(-32768) == 0);
872 
873     // unsigned -> signed
874     static assert(cast(NormalizedInt!byte)NormalizedInt!ubyte(0xFF) == 0x7F);
875     static assert(cast(NormalizedInt!byte)NormalizedInt!ubyte(0x83) == 0x41);
876     static assert(cast(NormalizedInt!short)NormalizedInt!ubyte(0xFF) == 0x7FFF);
877     static assert(cast(NormalizedInt!short)NormalizedInt!ubyte(0x83) == 0x41C1);
878     static assert(cast(NormalizedInt!byte)NormalizedInt!ushort(0xFFFF) == 0x7F);
879     static assert(cast(NormalizedInt!byte)NormalizedInt!ushort(0x83F7) == 0x41);
880 
881     // signed -> signed
882     static assert(cast(NormalizedInt!short)NormalizedInt!byte(127) == 32767);
883     static assert(cast(NormalizedInt!byte)NormalizedInt!short(32767) == 127);
884     static assert(cast(NormalizedInt!short)NormalizedInt!byte(-127) == -32767);
885     static assert(cast(NormalizedInt!byte)NormalizedInt!short(-32767) == -127);
886 }