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 }