From 8388f448f3f39f36157e60526ce951c823c87c28 Mon Sep 17 00:00:00 2001 From: thejinchao Date: Sat, 28 Jun 2025 18:32:37 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=96=B0=E7=9A=84iFloat?= =?UTF-8?q?=E6=9B=BF=E4=BB=A3=E5=8E=9F=E5=85=88=E7=9A=84ifloat=5Ft?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/core/CMakeLists.txt | 3 +- source/core/include/math/dvc_fix_point.h | 456 ++++++++++++++++++ source/core/include/math/dvc_fixmath.h | 98 ---- source/core/include/pipe/dvc_rasterizer.h | 16 +- source/core/source/math/dvc_fix_point.cpp | 20 + .../source/pipe/dvc_rasterizer_triangle.cpp | 187 +++++-- test/unit/CMakeLists.txt | 1 + test/unit/dvt_unit_common.h | 2 +- test/unit/dvt_unit_fix_point.cpp | 448 +++++++++++++++++ 9 files changed, 1089 insertions(+), 142 deletions(-) create mode 100644 source/core/include/math/dvc_fix_point.h delete mode 100644 source/core/include/math/dvc_fixmath.h create mode 100644 source/core/source/math/dvc_fix_point.cpp create mode 100644 test/unit/dvt_unit_fix_point.cpp diff --git a/source/core/CMakeLists.txt b/source/core/CMakeLists.txt index 1fdf73b..4d0cbf3 100644 --- a/source/core/CMakeLists.txt +++ b/source/core/CMakeLists.txt @@ -15,7 +15,7 @@ set(DV_CORE_MATH_INCLUDE_FILES include/math/dvc_matrix3.h include/math/dvc_matrix4.h include/math/dvc_math_util.h - include/math/dvc_fixmath.h + include/math/dvc_fix_point.h ) source_group("include/math" FILES ${DV_CORE_MATH_INCLUDE_FILES}) @@ -26,6 +26,7 @@ set(DV_CORE_MATH_SOURCE_FILES source/math/dvc_matrix3.cpp source/math/dvc_matrix4.cpp source/math/dvc_math_util.cpp + source/math/dvc_fix_point.cpp ) source_group("source/math" FILES ${DV_CORE_MATH_SOURCE_FILES}) diff --git a/source/core/include/math/dvc_fix_point.h b/source/core/include/math/dvc_fix_point.h new file mode 100644 index 0000000..2b26028 --- /dev/null +++ b/source/core/include/math/dvc_fix_point.h @@ -0,0 +1,456 @@ +#pragma once + +#include "dvc_config.h" + +DV_CORE_BEGIN_NAMESPACE + +class ifloat32 +{ +public: + static constexpr int32_t kShift = 16; + static constexpr float32_t kFloatScale = static_cast(1<> 1; + static constexpr int32_t kPI_s1516 = 0x3243f;//(int32_t)(M_PI * kFloatScale); + static constexpr int32_t kPi2_s1516 = 0x6487e; + static constexpr int32_t kPiHalf_s1516 = 0x1921f; + static constexpr int32_t kE_s1516 = 0x2b7e1; + + static constexpr int32_t kNegOne_s1516 = -1 << kShift; + + static constexpr int32_t kMax_s1516 = 0x7fffff80; //(int32_t)(nextafter(INT32_MAX/kFloatScale, 0.f)*kFloatScale); + static constexpr int32_t kMax_int32 = kMax_s1516 >> kShift; + static constexpr float32_t kMax_float = (float32_t)kMax_s1516 * kFloatInvScale; + + static constexpr int32_t kMin_s1516 = 0x80000000; // (int32_t)(nextafter(-INT32_MAX / ifloat32::kFloatScale, -FLT_MAX) * ifloat32::kFloatScale) + static constexpr int32_t kMin_int32 = kMin_s1516 >> kShift; + static constexpr float32_t kMin_float = (float32_t)kMin_s1516 * kFloatInvScale; + +public: + ifloat32() : s1516(0) {} + ifloat32(const ifloat32& other) : s1516(other.s1516) {} + + //Converts a float to a fixed-point value. + ifloat32(float32_t f) { + s1516 = static_cast(f * kFloatScale); + } + + //Converts a double to a fixed-point value. + ifloat32(float64_t f) { + s1516 = static_cast(f * kFloatScale); + } + + //Converts an integer to a fixed-point value. + ifloat32(int32_t n) { + s1516 = static_cast(n << kShift); + } + +public: + //Converts into a float. + float32_t to_float() const { + return static_cast(s1516 * kFloatInvScale); + } + + //Converts a fixed-point value into an integer by rounding it down to nearest integer. example: 3.94 -> 3, -2.1 -> -3 + int32_t to_int32() const { + return static_cast(s1516 >> kShift); + } + +public: + inline ifloat32& operator = (const ifloat32& other) { + s1516 = other.s1516; + return *this; + } + + inline bool operator == (const ifloat32& other) const { + return (s1516 == other.s1516); + } + + inline bool operator != (const ifloat32& other) const { + return (s1516 != other.s1516); + } + + inline bool operator < (const ifloat32& other) const { + return (s1516 < other.s1516); + } + + inline bool operator > (const ifloat32& other) const { + return (s1516 > other.s1516); + } + +public: + inline ifloat32& operator += (const ifloat32 other) { + s1516 += other.s1516; + return *this; + } + + inline ifloat32& operator -= (const ifloat32 other) { + s1516 -= other.s1516; + return *this; + } + +public: + //Adds the two FP numbers together. + inline friend ifloat32 operator + (const ifloat32& l, const ifloat32& r) { + int64_t t = static_cast(l.s1516) + static_cast(r.s1516); + + ifloat32 ret; + ret.s1516 = static_cast(t); + return ret; + } + + //Subtracts the two FP numbers from each other. + inline friend ifloat32 operator - (const ifloat32& l, const ifloat32& r) { + int64_t t = static_cast(l.s1516) - static_cast(r.s1516); + + ifloat32 ret; + ret.s1516 = t; + return ret; + } + + inline friend ifloat32 operator * (const ifloat32& a, const float32_t& b) { + int64_t t = static_cast(a.s1516) * static_cast(b * kFloatScale); + + t = t >> kShift; + + ifloat32 ret; + ret.s1516 = t; + return ret; + } + + inline friend ifloat32 operator * (const ifloat32& a, const int32_t& b) { + ifloat32 ret; + ret.s1516 = a.s1516 * b; + return ret; + } + + //Multiplies two FP values together. + inline friend ifloat32 operator * (const ifloat32& a, const ifloat32& b) { + int64_t t = static_cast(a.s1516) * static_cast(b.s1516); + + t = t >> kShift; + + ifloat32 ret; + ret.s1516 = static_cast(t); + return ret; + } + + inline friend ifloat32 operator / (const ifloat32& a, const ifloat32& b) { + // pre-multiply by the base + int64_t t = static_cast(a.s1516) << kShift; + t /= b.s1516; + + ifloat32 ret; + ret.s1516 = static_cast(t); + return ret; + } + +public: + int32_t s1516; +}; + +class ifloat64 +{ +public: + static constexpr int32_t kShift = 32; + static constexpr float32_t kFloatScale = static_cast(INT64_C(1) << kShift); //4294967296.0 + static constexpr float32_t kFloatInvScale = 1.0f / kFloatScale; + static constexpr int64_t kFractionMask = (INT64_C(1) << kShift) - 1; // Space before INT64_C(1) needed because of hacky C++ code generator + + //static_cast(nextafter(INT64_MAX / iFloat64::kFloatScale, 0.0) * iFloat64::kFloatScale); + static constexpr int64_t kMax_s3132 = INT64_C(0x7ffffffffffffc00); + static constexpr int32_t kMax_int32 = static_cast(kMax_s3132 >> kShift); + static constexpr float32_t kMax_float = 2.14748352e+09f; //nextafter(static_cast(kS3132_Max * kFloatInvScale), 0.0f); + + //(int32_t)(nextafter(-INT64_MAX / iFloat64::kFloatScale, -DBL_MAX) * iFloat64::kFloatScale); + static constexpr int64_t kMin_s3132 = INT64_C(0x8000000000000000); + static constexpr int32_t kMin_int32 = static_cast(kMin_s3132 >> kShift); + static constexpr float32_t kMin_float = static_cast(kMin_s3132 * kFloatInvScale); + + // special value + static DV_CORE_API const ifloat64 kMax; + static DV_CORE_API const ifloat64 kMin; + static DV_CORE_API const ifloat64 kZero; + static DV_CORE_API const ifloat64 kOne; + static DV_CORE_API const ifloat64 kTwo; + static DV_CORE_API const ifloat64 kThree; + static DV_CORE_API const ifloat64 kFour; + static DV_CORE_API const ifloat64 kHalf; + static DV_CORE_API const ifloat64 kPi; + static DV_CORE_API const ifloat64 kPi2; + static DV_CORE_API const ifloat64 kPiHalf; + static DV_CORE_API const ifloat64 kE; + static DV_CORE_API const ifloat64 kNegOne; + +public: + ifloat64() : s3132(0) {} + ifloat64(const ifloat64& other) : s3132(other.s3132) {} + + //Converts a float to a fixed-point value. + ifloat64(float32_t f) { + s3132 = static_cast(std::round(f * kFloatScale)); + } + + //Converts a double to a fixed-point value. + ifloat64(float64_t f) { + s3132 = static_cast(std::round(f * kFloatScale)); + } + + //Converts an integer to a fixed-point value. + ifloat64(int32_t n) { + s3132 = static_cast(n) << kShift; + } + +private: + ifloat64(int64_t _s3132) { + s3132 = _s3132; + } + +public: + //Converts into a float. + float32_t to_float() const { + return static_cast(s3132 * kFloatInvScale); + } + + //Converts a fixed-point value into an integer by rounding it down to nearest integer. example: 3.94 -> 3, -2.1 -> -3 + int32_t to_int32() const { + return static_cast(s3132 >> kShift); + } + +public: + inline const ifloat64& operator + () const { + return *this; + } + + inline ifloat64 operator - () const { + return ifloat64(-s3132); + } + + inline ifloat64& operator = (const ifloat64& other) { + s3132 = other.s3132; + return *this; + } + + inline bool operator == (const ifloat64& other) const { + return (s3132 == other.s3132); + } + + inline bool operator != (const ifloat64& other) const { + return (s3132 != other.s3132); + } + + inline bool operator < (const ifloat64& other) const { + return (s3132 < other.s3132); + } + + inline bool operator > (const ifloat64& other) const { + return (s3132 > other.s3132); + } + +public: + inline ifloat64& operator += (const ifloat64 other) { + s3132 += other.s3132; + return *this; + } + + inline ifloat64& operator -= (const ifloat64 other) { + s3132 -= other.s3132; + return *this; + } + +public: + //Adds the two FP numbers together. + inline friend ifloat64 operator + (const ifloat64& a, const ifloat64& b) { + ifloat64 ret; + ret.s3132 = a.s3132 + b.s3132; + return ret; + } + + //Subtracts the two FP numbers from each other. + inline friend ifloat64 operator - (const ifloat64& a, const ifloat64& b) { + ifloat64 ret; + ret.s3132 = a.s3132 - b.s3132; + return ret; + } + + inline friend ifloat64 operator * (const ifloat64& a, const int32_t& b) { + ifloat64 ret; + ret.s3132 = a.s3132 * b; + return ret; + } + + inline friend ifloat64 operator * (const ifloat64& a, const float32_t& b) { + ifloat64 ret; + ret.s3132 = _safeMulti(a.s3132, static_cast(b * kFloatScale)); + return ret; + } + + //Multiplies two FP values together. + inline friend ifloat64 operator * (const ifloat64& a, const ifloat64& b) { + ifloat64 ret; + ret.s3132 = _safeMulti(a.s3132, b.s3132); + return ret; + } + + inline friend ifloat64 operator / (const ifloat64& a, const ifloat64& b) { + // From http://www.hackersdelight.org/hdcodetxt/divlu.c.txt + + int64_t sign_dif = a.s3132 ^ b.s3132; + + static const uint64_t B = INT64_C(0x100000000); // Number base (32 bits) + + uint64_t abs_a = (uint64_t)((a.s3132 < 0) ? -a.s3132 : a.s3132); + uint64_t u1 = abs_a >> 32; + uint64_t u0 = abs_a << 32; + uint64_t v = (uint64_t)((b.s3132 < 0) ? -b.s3132 : b.s3132); + + // Overflow? + if (u1 >= v) + { + //invalid number + ifloat64 ret; + ret.s3132 = 0x7fffffffffffffff; + return ret; + } + + // Shift amount for norm + int32_t s = _nlz(v); // 0 <= s <= 63 + v = v << s; // Normalize the divisor + uint64_t vn1 = v >> 32; // Break the divisor into two 32-bit digits + uint64_t vn0 = v & INT64_C(0xffffffff); + + uint64_t un32 = (u1 << s) | (u0 >> (64 - s)) & (uint64_t)((int64_t)-s >> 63); + uint64_t un10 = u0 << s; // Shift dividend left + + uint64_t un1 = un10 >> 32; // Break the right half of dividend into two digits + uint64_t un0 = un10 & INT64_C(0xffffffff); + + // Compute the first quotient digit, q1 + uint64_t q1 = un32 / vn1; + uint64_t rhat = un32 - q1 * vn1; + do + { + if ((q1 >= B) || ((q1 * vn0) > (B * rhat + un1))) + { + q1 = q1 - 1; + rhat = rhat + vn1; + } + else break; + } while (rhat < B); + + uint64_t un21 = un32 * B + un1 - q1 * v; // Multiply and subtract + + // Compute the second quotient digit, q0 + uint64_t q0 = un21 / vn1; + rhat = un21 - q0 * vn1; + do + { + if ((q0 >= B) || ((q0 * vn0) > (B * rhat + un0))) + { + q0 = q0 - 1; + rhat = rhat + vn1; + } + else break; + } while (rhat < B); + + // Calculate the remainder + // uint64_t r = (un21 * b + un0 - q0 * v) >> s; + // rem = (int64_t)r; + + int64_t t = q1 * B + q0; + + ifloat64 ret; + ret.s3132 = (sign_dif < 0) ? -(int64_t)t : (int64_t)t; + return ret; + } + +public: + static int64_t _safeMulti(const int64_t a, const int64_t b) { + int64_t sign_diff = a ^ b; + uint64_t abs_a = a < 0 ? -a : a; + uint64_t abs_b = b < 0 ? -b : b; + + uint64_t ai = abs_a >> kShift; + uint64_t af = (abs_a & kFractionMask); + uint64_t bi = abs_b >> kShift; + uint64_t bf = (abs_b & kFractionMask); + + // (Ai+Af)*(Bi+Bf)=Af*Bf + Ai*(Bi+Bf) + Af*Bi + int64_t t = ((af * bf) >> kShift) + ai * abs_b + af * bi; + return (sign_diff < 0) ? -t : t; + } + + static int32_t _nlz(uint64_t x) + { + int32_t n = 0; + if (x <= INT64_C(0x00000000FFFFFFFF)) { n = n + 32; x = x << 32; } + if (x <= INT64_C(0x0000FFFFFFFFFFFF)) { n = n + 16; x = x << 16; } + if (x <= INT64_C(0x00FFFFFFFFFFFFFF)) { n = n + 8; x = x << 8; } + if (x <= INT64_C(0x0FFFFFFFFFFFFFFF)) { n = n + 4; x = x << 4; } + if (x <= INT64_C(0x3FFFFFFFFFFFFFFF)) { n = n + 2; x = x << 2; } + if (x <= INT64_C(0x7FFFFFFFFFFFFFFF)) { n = n + 1; } + if (x == 0) return 64; + return n; + } + +public: + int64_t s3132; +}; + +typedef ifloat32 iFloat32; +typedef ifloat64 iFloat64; + +typedef ifloat64 iFloat; + +struct iFloat2 +{ + iFloat x, y; + + iFloat2() : x(0), y(0) {} + iFloat2(const iFloat2& other) : x(other.x), y(other.y) {} + iFloat2(float32_t _x, float32_t _y) : x(_x), y(_y) {} +}; + +struct iFloat3 +{ + iFloat x, y, z; + + inline void add(const iFloat3& other) { + x += other.x; + y += other.y; + z += other.z; + } + + inline void sub(const iFloat3& other) { + x -= other.x; + y -= other.y; + z -= other.z; + } + + inline void mul(const iFloat& multiplier) { + x = x * multiplier; + y = y * multiplier; + z = z * multiplier; + } + + inline iFloat operator [] (const size_t i) const { + return *(&x + i); + } + + inline iFloat& operator [] (const size_t i) { + return *(&x + i); + } + + iFloat3() : x(0), y(0), z(0) {} + iFloat3(const iFloat3& other) : x(other.x), y(other.y), z(other.z) {} + iFloat3(float32_t _x, float32_t _y, float32_t _z) : x(_x), y(_y), z(_z) {} + iFloat3(iFloat _x, iFloat _y, iFloat _z) : x(_x), y(_y), z(_z) {} +}; + +DV_CORE_END_NAMESPACE diff --git a/source/core/include/math/dvc_fixmath.h b/source/core/include/math/dvc_fixmath.h deleted file mode 100644 index 2d4b21a..0000000 --- a/source/core/include/math/dvc_fixmath.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include "dvc_config.h" - -DV_CORE_BEGIN_NAMESPACE - -typedef int64_t ifloat_t; -//typedef int64_t ifloat2_t[2]; -//typedef int64_t ifloat3_t[3]; - -#define IFLOAT_SHIFT (16) -#define IFLOAT_SCALE ((float64_t)(1<> IFLOAT_SHIFT); -} - -//------------------------------------------------------------------------------------- -//inline void ifloat3_add(ifloat3_t& a, const ifloat3_t& b) -//{ -// a[0] += b[0]; a[1] += b[1]; a[2] += b[2]; -//} - -//------------------------------------------------------------------------------------- -//inline void ifloat3_sub(ifloat3_t& a, const ifloat3_t& b) -//{ -// a[0] -= b[0]; a[1] -= b[1]; a[2] -= b[2]; -//} - -//------------------------------------------------------------------------------------- -//inline void ifloat3_cpy(ifloat3_t& a, const ifloat3_t& b) -//{ -// a[0] = b[0]; a[1] = b[1]; a[2] = b[2]; -//} - -//------------------------------------------------------------------------------------- -struct ifloat2_t -{ - ifloat_t x, y; - - ifloat2_t() : x(0ll), y(0ll) {} - ifloat2_t(const ifloat2_t& other) : x(other.x), y(other.y) {} - ifloat2_t(float32_t _x, float32_t _y) : x(ifloat_init(_x)), y(ifloat_init(_y)) {} -}; - -struct ifloat3_t -{ - ifloat_t x, y, z; - - inline void add(const ifloat3_t& other) { - x += other.x; - y += other.y; - z += other.z; - } - - inline void sub(const ifloat3_t& other) { - x -= other.x; - y -= other.y; - z -= other.z; - } - - inline ifloat_t operator [] (const size_t i) const { - return *(&x + i); - } - - inline ifloat_t& operator [] (const size_t i) { - return *(&x + i); - } - - ifloat3_t() : x(0ll), y(0ll), z(0ll) {} - ifloat3_t(const ifloat3_t& other) : x(other.x), y(other.y), z(other.z) {} - ifloat3_t(float32_t _x, float32_t _y, float32_t _z) : x(ifloat_init(_x)), y(ifloat_init(_y)), z(ifloat_init(_z)) {} - ifloat3_t(ifloat_t _x, ifloat_t _y, ifloat_t _z) : x(_x), y(_y), z(_z) {} -}; - - -DV_CORE_END_NAMESPACE - diff --git a/source/core/include/pipe/dvc_rasterizer.h b/source/core/include/pipe/dvc_rasterizer.h index cd5298d..b399ff3 100644 --- a/source/core/include/pipe/dvc_rasterizer.h +++ b/source/core/include/pipe/dvc_rasterizer.h @@ -21,10 +21,24 @@ public: public: typedef std::function& pos, const fVector3& percent)> DrawTriangleCallback; + struct DebugParam + { + bool debug; + const char* szOutputFileName; + int32_t x; + int32_t y; + + FILE* fpOutput; + int32_t tile_id; + int32_t coarse_id; + int32_t fine_id; + int32_t edge_id; + }; + //Larrabee algorithm static void drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight, const fVector2& v0, const fVector2& v1, const fVector2& v2, - DrawTriangleCallback callback, bool ccw=true); + DrawTriangleCallback callback, bool ccw=true, const DebugParam* debugParam=nullptr); //Scaleline algorithm static void drawTriangleScanline(int32_t canvasWidth, int32_t canvasHeight, diff --git a/source/core/source/math/dvc_fix_point.cpp b/source/core/source/math/dvc_fix_point.cpp new file mode 100644 index 0000000..49f6607 --- /dev/null +++ b/source/core/source/math/dvc_fix_point.cpp @@ -0,0 +1,20 @@ +#include "math/dvc_fix_point.h" + +DV_CORE_BEGIN_NAMESPACE + +const ifloat64 ifloat64::kMax{ ifloat64::kMax_s3132 }; +const ifloat64 ifloat64::kMin{ ifloat64::kMin_s3132 }; +const ifloat64 ifloat64::kZero{ 0 }; +const ifloat64 ifloat64::kOne{ INT64_C(1) << ifloat64::kShift }; +const ifloat64 ifloat64::kTwo{ INT64_C(2) << kShift }; +const ifloat64 ifloat64::kThree{ INT64_C(3) << ifloat64::kShift }; +const ifloat64 ifloat64::kFour{ INT64_C(4) << ifloat64::kShift }; +const ifloat64 ifloat64::kHalf{ INT64_C(1) << (ifloat64::kShift-1) }; +const ifloat64 ifloat64::kPi{ INT64_C(0x3243f6a89) }; //static_cast(std::round(M_PI * kFloatScale)); +const ifloat64 ifloat64::kPi2{ INT64_C(0x6487ed511) }; +const ifloat64 ifloat64::kPiHalf{ INT64_C(0x1921fb544) }; +const ifloat64 ifloat64::kE{ INT64_C(0x2b7e15163) }; +const ifloat64 ifloat64::kNegOne{ INT64_C(-1) << kShift }; + +DV_CORE_END_NAMESPACE + diff --git a/source/core/source/pipe/dvc_rasterizer_triangle.cpp b/source/core/source/pipe/dvc_rasterizer_triangle.cpp index 2edfd1c..1ce8d2d 100644 --- a/source/core/source/pipe/dvc_rasterizer_triangle.cpp +++ b/source/core/source/pipe/dvc_rasterizer_triangle.cpp @@ -1,5 +1,5 @@ #include "pipe/dvc_rasterizer.h" -#include "math/dvc_fixmath.h" +#include "math/dvc_fix_point.h" #include "math/dvc_math_util.h" /* @@ -21,7 +21,8 @@ struct DrawTriangleParam float32_t bbox_max_y; bool tlBorder[3]; float32_t area; - ifloat3_t edgesDX, edgesDY; + iFloat3 edgesDX, edgesDY; + Rasterizer::DebugParam debugParam; }; //------------------------------------------------------------------------------------- @@ -29,10 +30,15 @@ enum { TILE_WIDTH_IN_PIXELS = 64, COARSE_BLOCK_WIDTH_IN_PIXELS = 16, FINE_BLOCK_ //------------------------------------------------------------------------------------- void _drawTriangle_Fine(int32_t tile_id, int32_t coarse_id, int32_t fine_id, - const DrawTriangleParam& param, const ifloat3_t& edges0, uint32_t testEdgeMask, + const DrawTriangleParam& param, const iFloat3& edges0, uint32_t testEdgeMask, Rasterizer::DrawTriangleCallback callback) { - ifloat3_t pixelEdges(edges0); + iFloat3 kHalfDX = param.edgesDX; kHalfDX.mul(iFloat::kHalf); + iFloat3 kHalfDY = param.edgesDY; kHalfDY.mul(iFloat::kHalf); + + iFloat3 pixelEdges(edges0); + pixelEdges.sub(kHalfDY); + pixelEdges.add(kHalfDX); const fVector2 v0(param.verts[0]), v1(param.verts[1]), v2(param.verts[2]); const bool edgeMask[3] = { (testEdgeMask & 1) != 0 , (testEdgeMask & 2) != 0 , (testEdgeMask & 4) != 0 }; @@ -45,10 +51,39 @@ void _drawTriangle_Fine(int32_t tile_id, int32_t coarse_id, int32_t fine_id, for (int32_t y_index=0, y= fine_start_y; y_index < 4; y_index++, y++) { - ifloat3_t edgesRow(pixelEdges); + iFloat3 edgesRow(pixelEdges); for (int32_t x_index = 0, x=fine_start_x; x_index < 4; x_index++, x++) { + if (param.debugParam.debug) + { + if (x == param.debugParam.x && y == param.debugParam.y) + { + iFloat3 LBEdges(edges0); + LBEdges.sub(kHalfDY); + LBEdges.add(kHalfDX); + + fprintf(param.debugParam.fpOutput, "======= Fine =======\n"); + fprintf(param.debugParam.fpOutput, "LeftBottom=[%d,%d](%f,%f)\n", fine_start_x, fine_start_y, fine_start_x+0.5f, fine_start_y+0.5f); + fprintf(param.debugParam.fpOutput, "Edge0=(%f,%f,%f)[%lld,%lld,%lld]\n", + edges0.x.to_float(), edges0.y.to_float(), edges0.z.to_float(), + edges0.x.s3132, edges0.y.s3132, edges0.z.s3132 + ); + fprintf(param.debugParam.fpOutput, "Edge0+0.5=(%f,%f,%f)[%lld,%lld,%lld]\n", + LBEdges.x.to_float(), LBEdges.y.to_float(), LBEdges.z.to_float(), + LBEdges.x.s3132, LBEdges.y.s3132, LBEdges.z.s3132 + ); + + fprintf(param.debugParam.fpOutput, "======= Pixel =======\n"); + fprintf(param.debugParam.fpOutput, "Position=[%d,%d](%f,%f)\n", x, y, x+0.5f, y+0.5f); + fprintf(param.debugParam.fpOutput, "IndexInFind=[%d,%d]\n", x_index, y_index); + fprintf(param.debugParam.fpOutput, "Edge=(%f,%f,%f)[%lld,%lld,%lld]\n", + edgesRow.x.to_float(), edgesRow.y.to_float(), edgesRow.z.to_float(), + edgesRow.x.s3132, edgesRow.y.s3132, edgesRow.z.s3132 + ); + } + } + bool rejected = (edgeMask[0] && (edgesRow.x < 0 || (!param.tlBorder[0] && edgesRow.x == 0))) || (edgeMask[1] && (edgesRow.y < 0 || (!param.tlBorder[1] && edgesRow.y == 0))) || @@ -74,24 +109,24 @@ void _drawTriangle_Fine(int32_t tile_id, int32_t coarse_id, int32_t fine_id, //------------------------------------------------------------------------------------- void _drawTriangle_Coarse(int32_t tile_id, int32_t coarse_id, - DrawTriangleParam& param, const ifloat3_t& edges0, uint32_t testEdgeMask, + DrawTriangleParam& param, const iFloat3& edges0, uint32_t testEdgeMask, Rasterizer::DrawTriangleCallback callback) { - const ifloat3_t blockEdgesDX( + const iFloat3 blockEdgesDX( param.edgesDX.x * FINE_BLOCK_WIDTH_IN_PIXELS, param.edgesDX.y * FINE_BLOCK_WIDTH_IN_PIXELS, param.edgesDX.z * FINE_BLOCK_WIDTH_IN_PIXELS ); - const ifloat3_t blockEdgesDY( + const iFloat3 blockEdgesDY( param.edgesDY.x * FINE_BLOCK_WIDTH_IN_PIXELS, param.edgesDY.y * FINE_BLOCK_WIDTH_IN_PIXELS, param.edgesDY.z * FINE_BLOCK_WIDTH_IN_PIXELS ); const bool edgeMask[3] = { (testEdgeMask & 1) != 0 , (testEdgeMask & 2) != 0 , (testEdgeMask & 4) != 0 }; - ifloat3_t blockReject(edges0); - ifloat3_t blockAccept(edges0); - ifloat3_t blockEdges(edges0); + iFloat3 blockReject(edges0); + iFloat3 blockAccept(edges0); + iFloat3 blockEdges(edges0); for (size_t v = 0; v < 3; v++) { @@ -117,8 +152,8 @@ void _drawTriangle_Coarse(int32_t tile_id, int32_t coarse_id, if (block_start_y + (y_index + 1)*FINE_BLOCK_WIDTH_IN_PIXELS >= param.bbox_min_y) { - ifloat3_t edgesRow(blockEdges); - ifloat3_t edgesRowReject, edgesRowAccept; + iFloat3 edgesRow(blockEdges); + iFloat3 edgesRowReject, edgesRowAccept; for (size_t v = 0; v < 3; v++) { @@ -182,25 +217,25 @@ void _drawTriangle_Coarse(int32_t tile_id, int32_t coarse_id, //------------------------------------------------------------------------------------- void _drawTriangle_Title(int32_t tile_id, - DrawTriangleParam& param, const ifloat3_t& edges0, uint32_t testEdgeMask, + DrawTriangleParam& param, const iFloat3& edges0, uint32_t testEdgeMask, Rasterizer::DrawTriangleCallback callback) { - const ifloat3_t blockEdgesDX( + const iFloat3 blockEdgesDX( param.edgesDX[0] * COARSE_BLOCK_WIDTH_IN_PIXELS, param.edgesDX[1] * COARSE_BLOCK_WIDTH_IN_PIXELS, param.edgesDX[2] * COARSE_BLOCK_WIDTH_IN_PIXELS ); - const ifloat3_t blockEdgesDY( + const iFloat3 blockEdgesDY( param.edgesDY[0] * COARSE_BLOCK_WIDTH_IN_PIXELS, param.edgesDY[1] * COARSE_BLOCK_WIDTH_IN_PIXELS, param.edgesDY[2] * COARSE_BLOCK_WIDTH_IN_PIXELS ); const bool edgeMask[3] = { (testEdgeMask & 1) != 0 , (testEdgeMask & 2) != 0 , (testEdgeMask & 4) != 0 }; - ifloat3_t blockReject(edges0); - ifloat3_t blockAccept(edges0); - ifloat3_t blockEdges(edges0); + iFloat3 blockReject(edges0); + iFloat3 blockAccept(edges0); + iFloat3 blockEdges(edges0); for (size_t v = 0; v < 3; v++) { @@ -226,8 +261,8 @@ void _drawTriangle_Title(int32_t tile_id, if (block_start_y + (y_index + 1)*COARSE_BLOCK_WIDTH_IN_PIXELS >= param.bbox_min_y) { - ifloat3_t edges(blockEdges); - ifloat3_t edgesRowReject, edgesRowAccept; + iFloat3 edges(blockEdges); + iFloat3 edgesRowReject, edgesRowAccept; for (size_t v = 0; v < 3; v++) { @@ -298,7 +333,7 @@ void _drawTriangle_Title(int32_t tile_id, //------------------------------------------------------------------------------------- void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight, const fVector2& v0, const fVector2& v1, const fVector2& v2, - DrawTriangleCallback callback, bool ccw) + DrawTriangleCallback callback, bool ccw, const DebugParam* debugParam) { assert(canvasWidth >= TILE_WIDTH_IN_PIXELS && canvasHeight >= TILE_WIDTH_IN_PIXELS); assert(canvasWidth%TILE_WIDTH_IN_PIXELS == 0 && canvasHeight%TILE_WIDTH_IN_PIXELS == 0); @@ -306,20 +341,23 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight, DrawTriangleParam param; param.widthInTiles = canvasWidth / TILE_WIDTH_IN_PIXELS; - //ccw + //Whether the three input vertices are in the agreed clockwise order or not, + // it indicates that the triangle is invisible bool isCCW = (v1 - v0).crossProduct(v2 - v1)>0; if (isCCW != ccw) return; + //By default, the order is counterclockwise. So if the input data is clockwise, a reversal is needed. + // Swap v1 and v2, and then reverse it back when the final callback is made param.ccw = ccw; param.verts[0] = v0; param.verts[1] = ccw ? v1 : v2; param.verts[2] = ccw ? v2 : v1; param.area = (param.verts[1] - param.verts[0]).crossProduct(param.verts[2] - param.verts[1]); - ifloat2_t vertices[3]; + iFloat2 vertices[3]; for (size_t i = 0; i < 3; i++) { - vertices[i] = ifloat2_t(param.verts[i].x, param.verts[i].y); + vertices[i] = iFloat2(param.verts[i].x, param.verts[i].y); } //get window coordinates bounding box @@ -329,8 +367,8 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight, param.bbox_max_y = MathUtil::max3(v0.y, v1.y, v2.y); //canvas size - ifloat_t iCanvasWidth = ifloat_init(canvasWidth); - ifloat_t iCanvasHeight = ifloat_init(canvasHeight); + iFloat iCanvasWidth(canvasWidth); + iFloat iCanvasHeight(canvasHeight); // clip triangles that are fully outside the scissor rect (scissor rect = whole window) if (param.bbox_max_x < 0 || param.bbox_max_y < 0 || param.bbox_min_x >= canvasWidth || param.bbox_min_y >= canvasHeight) @@ -348,13 +386,13 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight, int32_t last_tile_x = (int32_t)(param.bbox_max_x / TILE_WIDTH_IN_PIXELS); int32_t last_tile_y = (int32_t)(param.bbox_max_y / TILE_WIDTH_IN_PIXELS); - // evaluate edge equation at the top left tile - ifloat_t firstTileX = first_tile_x * ifloat_init(TILE_WIDTH_IN_PIXELS); - ifloat_t firstTileY = first_tile_y * ifloat_init(TILE_WIDTH_IN_PIXELS); + //The first Title is the Title at the lower left corner within the bounding box range covered by the triangle + iFloat firstTileX = iFloat(first_tile_x * TILE_WIDTH_IN_PIXELS); + iFloat firstTileY = iFloat(first_tile_y * TILE_WIDTH_IN_PIXELS); - ifloat3_t edges0; - ifloat3_t tileEdgesDX, tileEdgesDY, edgesReject, edgesAccept; - const ifloat_t kZeroPointFive = ifloat_init(0.5f); + iFloat3 edgesLB; //The edge value at the lower left corner of the first title + iFloat3 tileEdgesDX, tileEdgesDY; + iFloat3 edgesReject, edgesAccept; for (size_t v = 0; v < 3; v++) { @@ -366,8 +404,8 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight, tileEdgesDY[v] = param.edgesDY[v] * TILE_WIDTH_IN_PIXELS; //(V0,V1) X (V0, P) = (x1-x0)(py-y0)-(y1-y0)(px-x0) - edges0[v] = ifloat_multiply(param.edgesDX[v], (firstTileY + kZeroPointFive - vertices[v].y)) - - ifloat_multiply(param.edgesDY[v], (firstTileX + kZeroPointFive - vertices[v].x)); + edgesLB[v] = param.edgesDX[v] * (firstTileY - vertices[v].y) + - param.edgesDY[v] * (firstTileX - vertices[v].x); // Top-left rule: // shift top-left edges ever so slightly outward to make the top-left edges be @@ -382,7 +420,7 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight, param.tlBorder[v] = false; } - edgesReject[v] = edgesAccept[v] = edges0[v]; + edgesReject[v] = edgesAccept[v] = edgesLB[v]; if (tileEdgesDX[v] > 0) edgesReject[v] += tileEdgesDX[v]; else if (tileEdgesDX[v] < 0) edgesAccept[v] += tileEdgesDX[v]; @@ -391,18 +429,77 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight, else if (tileEdgesDY[v] < 0) edgesReject[v] -= tileEdgesDY[v]; } - ifloat3_t rowEdges(edges0); + if (debugParam != nullptr) + { + param.debugParam.debug = debugParam->debug; + param.debugParam.szOutputFileName = debugParam->szOutputFileName; + param.debugParam.x = debugParam->x; + param.debugParam.y = debugParam->y; + + if (param.debugParam.debug) { + param.debugParam.fpOutput = fopen(debugParam->szOutputFileName, "w"); + + fprintf(param.debugParam.fpOutput, "===== Triangle ====\n"); + for (int32_t i = 0; i < 3; i++) { + fprintf(param.debugParam.fpOutput, "v%d=(%f,%f)[%lld,%lld]\n", i, + param.verts[i].x, param.verts[i].y, vertices[i].x.s3132, vertices[i].y.s3132); + } + fprintf(param.debugParam.fpOutput, "===== TitleRange ====\n"); + fprintf(param.debugParam.fpOutput, + "tile_x={%d, %d}\ntile_y={%d, %d}\n", + first_tile_x, last_tile_x, first_tile_y, last_tile_y); + fprintf(param.debugParam.fpOutput, + "FirstTitle=(%f,%f)[%lld,%lld]\n", firstTileX.to_float(), firstTileY.to_float(), + firstTileX.s3132, firstTileY.s3132 + ); + + fprintf(param.debugParam.fpOutput, "===== Edge ====\n"); + for (int32_t i = 0; i < 3; i++) { + fprintf(param.debugParam.fpOutput, "DX%d=(%f)[%lld]\tDY%d=(%f)[%lld]\n", + i, + param.edgesDX[i].to_float(), param.edgesDX[i].s3132, + i, + param.edgesDY[i].to_float(), param.edgesDY[i].s3132); + } + fprintf(param.debugParam.fpOutput, "\n"); + for (int32_t i = 0; i < 3; i++) { + fprintf(param.debugParam.fpOutput, "Edge%d=(%lld)*(y-%lld)-(%lld)*(x-%lld)\n\t=(%f)*(y-%f)-(%f)*(x-%f)\n", i, + param.edgesDX[i].s3132, vertices[i].y.s3132, param.edgesDY[i].s3132, vertices[i].x.s3132, + param.edgesDX[i].to_float(), vertices[i].y.to_float(), param.edgesDY[i].to_float(), vertices[i].x.to_float() + ); + } + fprintf(param.debugParam.fpOutput, "EdgeLB=(%f,%f,%f)[%lld,%lld,%lld]\n", + edgesLB.x.to_float(), edgesLB.y.to_float(), edgesLB.z.to_float(), + edgesLB.x.s3132, edgesLB.y.s3132, edgesLB.z.s3132 + ); + for (int32_t i = 0; i < 3; i++) + { + fprintf(param.debugParam.fpOutput, "EdgeLB.%d=%lld*(%lld-%lld)-%lld*(%lld-%lld)\n", i, + param.edgesDX[i].s3132, firstTileY.s3132, vertices[i].y.s3132, + param.edgesDY[i].s3132, firstTileX.s3132, vertices[i].x.s3132 + ); + } + + fprintf(param.debugParam.fpOutput, "TopLeft=%s,%s,%s\n", + param.tlBorder[0] ? "true" : "false", param.tlBorder[1] ? "true" : "false", param.tlBorder[2] ? "true" : "false" + ); + } + } + + iFloat3 rowEdges(edgesLB); int32_t tile_row_start = first_tile_y * param.widthInTiles + first_tile_x; for (int32_t tile_y = first_tile_y; tile_y <= last_tile_y; tile_y++) { - ifloat3_t edges(rowEdges); - ifloat3_t tileEdgesReject(edgesReject); - ifloat3_t tileEdgesAccept(edgesAccept); + iFloat3 edges(rowEdges); + iFloat3 tileEdgesReject(edgesReject); + iFloat3 tileEdgesAccept(edgesAccept); int32_t tile_i = tile_row_start; for (int32_t tile_x = first_tile_x; tile_x <= last_tile_x; tile_x++) { + //If the title is excluded by any edge, + // it indicates that the title is completely outside the triangle and does not require rendering bool rejected = ((tileEdgesReject[0] < 0 || (!param.tlBorder[0] && tileEdgesReject[0] == 0))) || ((tileEdgesReject[1] < 0 || (!param.tlBorder[1] && tileEdgesReject[1] == 0))) || @@ -416,7 +513,9 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight, { if (tileEdgesAccept[v] < 0 || (tileEdgesAccept[v] == 0 && !param.tlBorder[v])) { - testEdgeMask += (1 << v); + //whether the accepting vertex of this title is on the "outside" of the three edges, + // that is, it may intersect or be completely outside + testEdgeMask += (1 << v); } } @@ -436,6 +535,12 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight, edgesAccept.add(tileEdgesDX); tile_row_start += param.widthInTiles; } + + if (debugParam != nullptr && debugParam->debug) + { + fclose(param.debugParam.fpOutput); + param.debugParam.fpOutput = nullptr; + } } //------------------------------------------------------------------------------------- diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 54ab28b..4d10214 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -13,6 +13,7 @@ set(DVT_UNIT_SOURCE_FILES dvt_unit_vector4.cpp dvt_unit_matrix3.cpp dvt_unit_matrix4.cpp + dvt_unit_fix_point.cpp dvt_unit_math_util.cpp dvt_unit_rasterizer.cpp ) diff --git a/test/unit/dvt_unit_common.h b/test/unit/dvt_unit_common.h index 166407c..fe077a4 100644 --- a/test/unit/dvt_unit_common.h +++ b/test/unit/dvt_unit_common.h @@ -6,7 +6,7 @@ using namespace davinci; //------------------------------------------------------------------------------------- inline float _randomFloat(void) { - return MathUtil::rangeRandom(-65521 / 16.f, 65521 / 16.f); + return MathUtil::rangeRandom(-65521 / 24.f, 65521 / 24.f); } //------------------------------------------------------------------------------------- diff --git a/test/unit/dvt_unit_fix_point.cpp b/test/unit/dvt_unit_fix_point.cpp new file mode 100644 index 0000000..7043576 --- /dev/null +++ b/test/unit/dvt_unit_fix_point.cpp @@ -0,0 +1,448 @@ +#include "math/dvc_fix_point.h" +#include "dvt_unit_common.h" + +#define _USE_MATH_DEFINES +#include +#include + +using namespace davinci; + +TEST(FixPoint32, Basic) +{ + iFloat32 a; + EXPECT_EQ(a.s1516, 0); + + a = iFloat32(0); + EXPECT_EQ(a.s1516, iFloat32::kZero_s1516); + EXPECT_EQ(a.to_float(), 0.f); + EXPECT_EQ(a.to_int32(), 0); + + a = iFloat32(0.5f); + EXPECT_EQ(a.s1516, iFloat32::kHalf_s1516); + EXPECT_EQ(a.to_float(), 0.5f); + EXPECT_EQ(a.to_int32(), 0); + + a = iFloat32(1.0f); + EXPECT_EQ(a.s1516, iFloat32::kOne_s1516); + EXPECT_EQ(a.to_float(), 1.f); + EXPECT_EQ(a.to_int32(), 1); + + a = iFloat32(-0.5f); + EXPECT_EQ(a.s1516, -iFloat32::kHalf_s1516); + EXPECT_EQ(a.to_float(), -0.5f); + EXPECT_EQ(a.to_int32(), -1); + + a = iFloat32(-1.f); + EXPECT_EQ(a.s1516, iFloat32::kNegOne_s1516); + EXPECT_EQ(a.to_float(), -1.f); + EXPECT_EQ(a.to_int32(), -1); + + a = iFloat32(1); + EXPECT_EQ(a.s1516, iFloat32::kOne_s1516); + EXPECT_EQ(a.to_float(), 1.f); + EXPECT_EQ(a.to_int32(), 1); + + a = iFloat32(-1); + EXPECT_EQ(a.s1516, iFloat32::kNegOne_s1516); + EXPECT_EQ(a.to_float(), -1.f); + EXPECT_EQ(a.to_int32(), -1); + + a = iFloat32(M_PI); //3.14 + EXPECT_EQ(a.s1516, (int32_t)(M_PI * iFloat32::kFloatScale)); + EXPECT_EQ(a.to_float(), ((int32_t)(M_PI * iFloat32::kFloatScale)) / (iFloat32::kFloatScale)); + EXPECT_EQ(a.to_int32(), 3); + + a = iFloat32(-M_PI); //-3.14 + EXPECT_EQ(a.s1516, -(int32_t)(M_PI * iFloat32::kFloatScale)); + EXPECT_EQ(a.to_float(), -((int32_t)(M_PI * iFloat32::kFloatScale)) / (iFloat32::kFloatScale)); + EXPECT_EQ(a.to_int32(), -4); + + a = iFloat32(-M_E); //-2.718 + EXPECT_EQ(a.s1516, -(int32_t)(M_E * iFloat32::kFloatScale)); + EXPECT_EQ(a.to_float(), -((int32_t)(M_E * iFloat32::kFloatScale)) / (iFloat32::kFloatScale)); + EXPECT_EQ(a.to_int32(), -3); +} + +TEST(FixPoint32, Range) +{ + iFloat32 a; + + a = iFloat32(iFloat32::kMax_float); + EXPECT_EQ(a.s1516, iFloat32::kMax_s1516); + EXPECT_EQ(a.to_float(), iFloat32::kMax_float); + EXPECT_EQ(a.to_int32(), iFloat32::kMax_int32); + + a = iFloat32(iFloat32::kMax_float + 0.1f); + EXPECT_LT(a.s1516, 0); + EXPECT_LT(a.to_float(), 0); + EXPECT_LT(a.to_int32(), 0); + + //a = iFloat32(iFloat32::kMax_int32); + //EXPECT_LT(a.s1516, iFloat32::kMax_s1516); + //EXPECT_LT(a.to_float(), iFloat32::kMax_float); + //EXPECT_EQ(a.to_int32(), iFloat32::kMax_int32); + + a = iFloat32(iFloat32::kMax_int32 + 1); + EXPECT_LT(a.s1516, 0); + EXPECT_LT(a.to_float(), 0); + EXPECT_LT(a.to_int32(), 0); + + a = iFloat32(iFloat32::kMin_float); + EXPECT_EQ(a.s1516, iFloat32::kMin_s1516); + EXPECT_EQ(a.to_float(), iFloat32::kMin_float); + EXPECT_EQ(a.to_int32(), iFloat32::kMin_int32); + + a = iFloat32(iFloat32::kMin_float-1.f); + EXPECT_EQ(a.s1516, iFloat32::kMin_s1516); + EXPECT_EQ(a.to_float(), iFloat32::kMin_float); + EXPECT_EQ(a.to_int32(), iFloat32::kMin_int32); + + a = iFloat32(iFloat32::kMin_int32); + EXPECT_EQ(a.s1516, iFloat32::kMin_s1516); + EXPECT_EQ(a.to_float(), iFloat32::kMin_float); + EXPECT_EQ(a.to_int32(), iFloat32::kMin_int32); + + a = iFloat32(iFloat32::kMin_int32 - 1); + EXPECT_GT(a.s1516, 0); + EXPECT_GT(a.to_float(), 0); + EXPECT_GT(a.to_int32(), 0); +} + +TEST(FixPoint32, Compare) +{ + float a = _randomFloat(); + iFloat32 ia = a; + EXPECT_EQ(ia.to_float(), a); + + int32_t n = MathUtil::rangeRandom(iFloat32::kMin_int32, iFloat32::kMax_int32); + iFloat32 in = n; + EXPECT_EQ(in.to_int32(), n); + + float f1 = _randomFloat(), f2 = f1 + 1.f; + iFloat32 iF1 = f1, iF2 = f2; + + EXPECT_LT(iF1, iF2); + EXPECT_GT(iF2, iF1); + EXPECT_NE(iF1, iF2); + EXPECT_EQ(iF1, iF1); + + iF1 += iF2; + EXPECT_EQ(iF1, f1 + f2); + + iF1 -= iF2; + EXPECT_EQ(iF1, f1); +} + +TEST(FixPoint32, Operation) +{ + for (size_t i = 0; i < 500; i++) + { + float a = _randomFloat(); + float b = _randomFloat(); + + iFloat32 ia(a), ib(b); + iFloat32 ret = ia + ib; + + EXPECT_EQ(ret.to_float(), a + b); + + iFloat32 ic(a); + iFloat32 ret2 = ic + b; + EXPECT_EQ(ret2.to_float(), a + b); + } + + for (size_t i = 0; i < 500; i++) + { + float a = _randomFloat(); + float b = _randomFloat(); + + iFloat32 ia(a), ib(b); + iFloat32 ret = ia - ib; + + EXPECT_EQ(ret.to_float(), a - b); + + iFloat32 ic(a); + iFloat32 ret2 = ic - b; + EXPECT_EQ(ret2.to_float(), a - b); + } + + for (size_t i = 0; i < 500; i++) + { + float32_t a, b; + int32_t c; + float32_t ret1, ret2; + do { + a = _randomFloat(); + b = _randomFloat(); + + ret1 = a * b; + if (ret1iFloat32::kMax_float) { + continue; + } + + c = rand() & 0xFFF; + ret2 = a * c; + if (ret2iFloat32::kMax_float) { + continue; + } + break; + } while (true); + + iFloat32 ia(a), ib(b); + iFloat32 iret1 = ia * ib; + EXPECT_LE(std::abs(iret1.to_float() - ret1), 0.01f); + + iFloat32 iret2 = ia * b; + EXPECT_EQ(iret2, iret1); + + iFloat32 iret3 = ia * c; + EXPECT_EQ(iret3.to_float(), ret2); + } + + for (size_t i = 0; i < 500; i++) + { + float a, b, ret; + a = _randomFloat(); + b = _randomFloat(); + ret = a / b; + + if (ret < iFloat32::kMin_float) ret = iFloat32::kMin_float; + else if (ret > iFloat32::kMax_float) ret = iFloat32::kMax_float; + + iFloat32 ia(a), ib(b); + iFloat32 iret = ia / ib; + + EXPECT_LE(std::abs(iret.to_float() - ret), 0.001f); + } +} + +TEST(FixPoint64, Basic) +{ + iFloat64 a; + EXPECT_EQ(a.s3132, 0); + + a = iFloat64(0); + EXPECT_EQ(a.s3132, iFloat64::kZero.s3132); + EXPECT_EQ(a.to_float(), 0.f); + EXPECT_EQ(a.to_int32(), 0); + + a = iFloat64(0.5f); + EXPECT_EQ(a, iFloat64::kHalf); + EXPECT_EQ(a.s3132, iFloat64::kHalf.s3132); + EXPECT_EQ(a.to_float(), 0.5f); + EXPECT_EQ(a.to_int32(), 0); + + a = iFloat64(1.0f); + EXPECT_EQ(a, iFloat64::kOne); + EXPECT_EQ(a.s3132, iFloat64::kOne.s3132); + EXPECT_EQ(a.to_float(), 1.f); + EXPECT_EQ(a.to_int32(), 1); + + a = iFloat64(-0.5f); + EXPECT_EQ(a, -iFloat64::kHalf); + EXPECT_EQ(a.s3132, -iFloat64::kHalf.s3132); + EXPECT_EQ(a.to_float(), -0.5f); + EXPECT_EQ(a.to_int32(), -1); + + a = iFloat64(-1.f); + EXPECT_EQ(a, -iFloat64::kOne); + EXPECT_EQ(a.s3132, iFloat64::kNegOne.s3132); + EXPECT_EQ(a.to_float(), -1.f); + EXPECT_EQ(a.to_int32(), -1); + + a = iFloat64(1); + EXPECT_EQ(a, iFloat64::kOne); + EXPECT_EQ(a.s3132, iFloat64::kOne.s3132); + EXPECT_EQ(a.to_float(), 1.f); + EXPECT_EQ(a.to_int32(), 1); + + a = iFloat64(-1); + EXPECT_EQ(a, -iFloat64::kOne); + EXPECT_EQ(a.s3132, iFloat64::kNegOne.s3132); + EXPECT_EQ(a.to_float(), -1.f); + EXPECT_EQ(a.to_int32(), -1); + + a = iFloat64(M_PI); //3.14 + EXPECT_EQ(a, iFloat64::kPi); + EXPECT_EQ(a.s3132, iFloat64::kPi.s3132); + EXPECT_EQ(a.to_float(), (float32_t)((int64_t)(M_PI * iFloat64::kFloatScale)) / (iFloat64::kFloatScale)); + EXPECT_EQ(a.to_int32(), 3); + + a = iFloat64(M_PI*2); //2*3.14 + EXPECT_EQ(a, iFloat64::kPi2); + EXPECT_EQ(a.s3132, iFloat64::kPi2.s3132); + EXPECT_EQ(a.to_float(), (float32_t)((int64_t)(2 * M_PI * iFloat64::kFloatScale)) / (iFloat64::kFloatScale)); + EXPECT_EQ(a.to_int32(), 6); + + a = iFloat64(M_PI * 0.5); //0.5*3.14 + EXPECT_EQ(a, iFloat64::kPiHalf); + EXPECT_EQ(a.s3132, iFloat64::kPiHalf.s3132); + EXPECT_EQ(a.to_float(), (float32_t)((int64_t)(0.5 * M_PI * iFloat64::kFloatScale)) / (iFloat64::kFloatScale)); + EXPECT_EQ(a.to_int32(), 1); + + a = iFloat64(-M_PI); //-3.14 + EXPECT_EQ(a, -iFloat64::kPi); + EXPECT_EQ(a.s3132, -iFloat64::kPi.s3132); + EXPECT_EQ(a.to_float(), -(float32_t)((int64_t)(M_PI * iFloat64::kFloatScale)) / (iFloat64::kFloatScale)); + EXPECT_EQ(a.to_int32(), -4); + + a = iFloat64(M_E); //2.718 + EXPECT_EQ(a, iFloat64::kE); + EXPECT_EQ(a.s3132, iFloat64::kE.s3132); + EXPECT_EQ(a.to_float(), (float32_t)((int64_t)(M_E * iFloat64::kFloatScale)) / (iFloat64::kFloatScale)); + EXPECT_EQ(a.to_int32(), 2); + + a = iFloat64(-M_E); //-2.718 + EXPECT_EQ(a, -iFloat64::kE); + EXPECT_EQ(a.s3132, -iFloat64::kE.s3132); + EXPECT_EQ(a.to_float(), -(float32_t)((int64_t)(M_E * iFloat64::kFloatScale)) / (iFloat64::kFloatScale)); + EXPECT_EQ(a.to_int32(), -3); +} + +TEST(FixPoint64, Range) +{ + iFloat64 a; + + static constexpr int32_t kShift = 32; + static constexpr float64_t kFloatScale = static_cast(1LL << kShift); //4294967296.0 + static constexpr float64_t kFloatInvScale = 1.0 / kFloatScale; + + a = iFloat64(iFloat64::kMax_float); + EXPECT_LE(a.s3132, iFloat64::kMax_s3132); + EXPECT_LE(a.to_float(), iFloat64::kMax_float); + EXPECT_LE(a.to_int32(), iFloat64::kMax_int32); + + a = iFloat64(nextafter(iFloat64::kMax_float, FLT_MAX)); + EXPECT_LT(a.s3132, 0); + EXPECT_LT(a.to_float(), 0.f); + EXPECT_LT(a.to_int32(), 0); + + a = iFloat64(iFloat64::kMax_int32); + EXPECT_LE(a.s3132, iFloat64::kMax_s3132); + EXPECT_LE(a.to_int32(), iFloat64::kMax_int32); + + a = iFloat64(iFloat64::kMax_int32+1); + EXPECT_LT(a.s3132, 0); + EXPECT_LT(a.to_float(), 0.f); + EXPECT_LT(a.to_int32(), 0); + + a = iFloat64(iFloat64::kMin_float); + EXPECT_EQ(a.s3132, iFloat64::kMin_s3132); + EXPECT_EQ(a.to_float(), iFloat64::kMin_float); + EXPECT_EQ(a.to_int32(), iFloat64::kMin_int32); + + a = iFloat64(iFloat64::kMin_float - 1.f); + EXPECT_EQ(a.s3132, iFloat64::kMin_s3132); + EXPECT_EQ(a.to_float(), iFloat64::kMin_float); + EXPECT_EQ(a.to_int32(), iFloat64::kMin_int32); + + a = iFloat64(iFloat64::kMin_int32); + EXPECT_EQ(a.s3132, iFloat64::kMin_s3132); + EXPECT_EQ(a.to_float(), iFloat64::kMin_float); + EXPECT_EQ(a.to_int32(), iFloat64::kMin_int32); + + a = iFloat64(iFloat64::kMin_int32 - 1); + EXPECT_GT(a.s3132, 0); + EXPECT_GT(a.to_float(), 0); + EXPECT_GT(a.to_int32(), 0); + + a = iFloat64(iFloat64::kMax_float - 100.f) + iFloat64(iFloat64::kMax_float - 200.f); + EXPECT_LT(a.s3132, 0); + EXPECT_LT(a.to_float(), 0.f); + EXPECT_LT(a.to_int32(), 0); + + a = iFloat64(iFloat64::kMin_float + 100.f) - iFloat64(iFloat64::kMax_float + 200.f); + EXPECT_GT(a.s3132, 0); + EXPECT_GT(a.to_float(), 0.f); + EXPECT_GT(a.to_int32(), 0); +} + +TEST(FixPoint64, Compare) +{ + float a = _randomFloat(); + iFloat64 ia = a; + EXPECT_EQ(ia.to_float(), a); + + int32_t n = MathUtil::rangeRandom(iFloat64::kMin_int32, iFloat64::kMax_int32); + iFloat64 in = n; + EXPECT_EQ(in.to_int32(), n); + + float f1 = _randomFloat(), f2 = f1 + 1.f; + iFloat64 iF1 = f1, iF2 = f2; + + EXPECT_LT(iF1, iF2); // < + EXPECT_GT(iF2, iF1); // > + EXPECT_NE(iF1, iF2); // != + EXPECT_EQ(iF1, iF1); // == + + iF1 += iF2; + EXPECT_EQ(iF1, f1 + f2); + + iF1 -= iF2; + EXPECT_EQ(iF1, f1); +} + +TEST(FixPoint64, Operation) +{ + for (size_t i = 0; i < 500; i++) + { + float a = _randomFloat(); + float b = _randomFloat(); + + iFloat64 ia(a), ib(b); + iFloat64 ret = ia + ib; + + EXPECT_EQ(ret.to_float(), a + b); + + iFloat64 ic(a); + iFloat64 ret2 = ic + b; + EXPECT_EQ(ret2.to_float(), a + b); + } + + for (size_t i = 0; i < 500; i++) + { + float a = _randomFloat(); + float b = _randomFloat(); + + iFloat64 ia(a), ib(b); + iFloat64 ret = ia - ib; + + EXPECT_EQ(ret.to_float(), a - b); + + iFloat64 ic(a); + iFloat64 ret2 = ic - b; + EXPECT_EQ(ret2.to_float(), a - b); + } + + for (size_t i = 0; i < 500; i++) + { + float32_t a = _randomFloat(); + float32_t b = _randomFloat(); + int32_t c = rand() & 0xFFFF; + + // iFloat*iFloat + iFloat64 ia(a), ib(b); + iFloat64 iret = ia * ib; + EXPECT_EQ(iret.to_float(), a*b); + + // iFloat*int + iFloat64 iret2 = ia * c; + EXPECT_EQ(iret2.to_float(), a * c); + + // iFloat*float + iFloat64 iret3 = ia * b; + EXPECT_EQ(iret3, iret); + } + + for (size_t i = 0; i < 500; i++) + { + float a, b, ret; + a = _randomFloat(); + b = _randomFloat(); + ret = a / b; + + iFloat64 ia(a), ib(b); + iFloat64 iret = ia / ib; + + EXPECT_LE(std::abs(iret.to_float() - ret), 0.001f); + } +}