Normalized!byte b; // Behaves like a float, but stores -1..1 in a byte as -127..127. Normalized!uint u; // Stores 0..1 in a uint as 0..uint.max. // Just use them as if they are floats. b = 0.5; u = 0.2; b *= u; u = b * u + 0.1; assert(b < u); assert(b + 0.05 > u); // They are automatically capped at 0..1 for unsigned and -1..1 for signed. b += 3.5; assert(b == 1); b = 10 * -0.3; u = -2.4; assert(b == -1); assert(u == 0); // .raw gives access to the underlying storage. b = 0.5; assert(b.raw == 63); b.raw = -127; assert(b == -1); u = 0; assert(u.raw == 0); u.raw = 2147483648; assert(u >= 0.49 && u <= 0.51); // fromRaw constructs from the raw value. auto x = Normalized!ubyte(0.4); auto y = Normalized!ubyte.fromRaw(102); // 102/255 assert(x == y); // The floating point type it behaves as is the smallest of [float, double, real] with // at least as much precision as the underlying type. static assert(is(Normalized!byte.float_type == float)); static assert(is(Normalized!uint.float_type == double)); static assert(is(Normalized!long.float_type == real)); // Normalized!T only contains a T, so a T[] and a Normalized!T[] can be // casted to eachother. static assert(Normalized!byte.sizeof == 1); ubyte[3] rgb_b = [255, 102, 0]; auto rgb_f = cast(Normalized!ubyte[])rgb_b; // rgb_f is now [1, 0.4, 0] assert(rgb_f[0] == 1); rgb_f[1] = 0.6; // Modifies the original byte in rgb_b. assert(rgb_b[1] == 153);
An integral type behaving like a floating point type.