KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_wide_int.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <cstdint>
23#include <limits>
24#include <random>
25
27#include <math/wide_int.h>
28
29BOOST_AUTO_TEST_SUITE( WideInt )
30
31namespace
32{
33int sign( const KI_INT128& aValue )
34{
35 return ( aValue > KI_INT128( 0 ) ) - ( aValue < KI_INT128( 0 ) );
36}
37} // namespace
38
39
40BOOST_AUTO_TEST_CASE( CrossWideKnownValues )
41{
42 BOOST_CHECK_EQUAL( ToDouble( CrossWide( VECTOR2L( 3, 4 ), VECTOR2L( 5, 6 ) ) ), -2.0 );
43 BOOST_CHECK_EQUAL( ToDouble( DotWide( VECTOR2L( 3, 4 ), VECTOR2L( 5, 6 ) ) ), 39.0 );
44
45 // The (+-1.1e9) diagonals wrap an int64 cross product
46 const VECTOR2L d1( 2200000000LL, 2200000000LL );
47 const VECTOR2L d2( 2200000000LL, -2200000000LL );
48
49 BOOST_CHECK_EQUAL( sign( CrossWide( d1, d2 ) ), -1 );
50 BOOST_CHECK_EQUAL( sign( CrossWide( d2, d1 ) ), 1 );
51 BOOST_CHECK_EQUAL( sign( CrossWide( d1, d1 ) ), 0 );
52 BOOST_CHECK_EQUAL( ToDouble( CrossWide( d1, d2 ) ), -2.0 * 2200000000.0 * 2200000000.0 );
53
54 // Products differing by 1, invisible to a double product
55 const int64_t k = 3000000000LL;
56
57 BOOST_CHECK_EQUAL( ToDouble( CrossWide( VECTOR2L( k + 1, k ), VECTOR2L( k + 2, k + 1 ) ) ), 1.0 );
58 BOOST_CHECK_EQUAL( ToDouble( CrossWide( VECTOR2L( k + 2, k + 1 ), VECTOR2L( k + 1, k ) ) ), -1.0 );
59}
60
61
62BOOST_AUTO_TEST_CASE( Int64Extremes )
63{
64 constexpr int64_t lo = std::numeric_limits<int64_t>::min();
65 constexpr int64_t hi = std::numeric_limits<int64_t>::max();
66
67 // (-2^63)^2 - 0 = 2^126
68 BOOST_CHECK_EQUAL( ToDouble( CrossWide( VECTOR2L( lo, 0 ), VECTOR2L( 0, lo ) ) ), std::ldexp( 1.0, 126 ) );
69 BOOST_CHECK_EQUAL( ToDouble( CrossWide( VECTOR2L( lo, 0 ), VECTOR2L( 0, hi ) ) ), -std::ldexp( 1.0, 126 ) );
70 BOOST_CHECK_EQUAL( ToDouble( DotWide( VECTOR2L( lo, 0 ), VECTOR2L( lo, 0 ) ) ), std::ldexp( 1.0, 126 ) );
71}
72
73
74BOOST_AUTO_TEST_CASE( OrientationSignBeyondDoublePrecision )
75{
76 // The exact determinant is 1 but a double cross product rounds it to 0
77 VECTOR2I a( -1500000000, -1500000000 );
78 VECTOR2I b( 1500000001, 1500000000 );
79 VECTOR2I c( 1500000002, 1500000001 );
80
83}
84
85
86BOOST_AUTO_TEST_CASE( InCircleDelaunayLegalExact )
87{
88 const VECTOR2I a( 0, 0 ), b( 1000000, 0 ), c( 0, 1000000 );
89
90 // Cocircular is legal, inside is not, outside is
91 BOOST_CHECK( KIGEOM::InCircleDelaunayLegal( a, b, c, VECTOR2I( 1000000, 1000000 ) ) );
92 BOOST_CHECK( !KIGEOM::InCircleDelaunayLegal( a, b, c, VECTOR2I( 999999, 999999 ) ) );
93 BOOST_CHECK( KIGEOM::InCircleDelaunayLegal( a, b, c, VECTOR2I( 1000001, 1000001 ) ) );
94
95 // Exact determinant is 999999999000000000, far below the tolerance of a filtered double test
96 BOOST_CHECK( !KIGEOM::InCircleDelaunayLegal( VECTOR2I( 0, 0 ), VECTOR2I( 1000000000, 0 ),
97 VECTOR2I( 1000000000, 1 ), VECTOR2I( 1, 1 ) ) );
98}
99
100
101BOOST_AUTO_TEST_CASE( WordsToDoubleBorrowAndRounding )
102{
104
105 BOOST_CHECK_EQUAL( WordsToDouble( 0, 0 ), 0.0 );
106 BOOST_CHECK_EQUAL( WordsToDouble( 0, UINT64_MAX ), 18446744073709551616.0 );
107 BOOST_CHECK_EQUAL( WordsToDouble( 1, 0 ), std::ldexp( 1.0, 64 ) );
108 BOOST_CHECK_EQUAL( WordsToDouble( -1, 0 ), -std::ldexp( 1.0, 64 ) );
109 BOOST_CHECK_EQUAL( WordsToDouble( -1, UINT64_MAX ), -1.0 );
110
111 // 2^64 + 2^11 + 1 is above the halfway point of a 53-bit mantissa, only the sticky bit rounds it up
112 const double up = std::ldexp( 1.0, 64 ) + std::ldexp( 1.0, 12 );
113
114 BOOST_CHECK_EQUAL( WordsToDouble( 1, ( uint64_t( 1 ) << 11 ) + 1 ), up );
115 BOOST_CHECK_EQUAL( WordsToDouble( -2, ~( ( uint64_t( 1 ) << 11 ) + 1 ) + 1 ), -up );
116
117 // Exactly halfway rounds to even
118 BOOST_CHECK_EQUAL( WordsToDouble( 1, uint64_t( 1 ) << 11 ), std::ldexp( 1.0, 64 ) );
119}
120
121
122BOOST_AUTO_TEST_CASE( RandomAgainstLongDouble )
123{
124 std::mt19937_64 rng( 4242 );
125
126 auto operand = [&]() -> int64_t
127 {
128 int64_t v = static_cast<int64_t>( rng() );
129
130 return v >> ( rng() % 64 );
131 };
132
133 for( int i = 0; i < 100000; i++ )
134 {
135 VECTOR2L a( operand(), operand() );
136 VECTOR2L b( operand(), operand() );
137
138 // The x87 mantissa has 64 bits so each product is exact and the difference is close
139 long double cross = static_cast<long double>( a.x ) * b.y - static_cast<long double>( a.y ) * b.x;
140 long double dot = static_cast<long double>( a.x ) * b.x + static_cast<long double>( a.y ) * b.y;
141
142 BOOST_REQUIRE_CLOSE_FRACTION( ToDouble( CrossWide( a, b ) ), static_cast<double>( cross ), 1e-9 );
143 BOOST_REQUIRE_CLOSE_FRACTION( ToDouble( DotWide( a, b ) ), static_cast<double>( dot ), 1e-9 );
144 }
145}
146
147
148#if defined( __SIZEOF_INT128__ ) && !defined( _MSC_VER )
149
150BOOST_AUTO_TEST_CASE( WordsToDoubleAgainstInt128 )
151{
152 std::mt19937_64 rng( 987 );
153
154 for( int i = 0; i < 300000; i++ )
155 {
156 unsigned __int128 mag = ( static_cast<unsigned __int128>( rng() ) << 64 ) | rng();
157 mag >>= rng() % 128;
158
159 // Sticky and tie patterns need many trailing zeros
160 if( i % 3 == 0 )
161 mag &= ~( ( static_cast<unsigned __int128>( 1 ) << ( rng() % 100 ) ) - 1 );
162
163 __int128 v = static_cast<__int128>( mag );
164
165 BOOST_REQUIRE_EQUAL( KIGEOM_WIDE::WordsToDouble( static_cast<int64_t>( v >> 64 ), static_cast<uint64_t>( v ) ),
166 static_cast<double>( v ) );
167 }
168}
169
170#endif
171
Exact orientation and in-circle predicates over integer coordinates.
static thread_local boost::mt19937 rng
Definition kiid.cpp:49
double WordsToDouble(int64_t aHi, uint64_t aLo)
Nearest double to hi * 2^64 + lo, ties to even.
Definition wide_int.h:46
int OrientationSign(const VECTOR2I &a, const VECTOR2I &b, const VECTOR2I &c)
Orientation of triangle (a, b, c): +1 counter-clockwise, -1 clockwise, 0 collinear.
bool InCircleDelaunayLegal(const VECTOR2I &a, const VECTOR2I &b, const VECTOR2I &c, const VECTOR2I &p)
True when p is outside the circumcircle of CCW triangle (a, b, c): the shared edge is already Delauna...
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_EQUAL(result, "25.4")
BOOST_AUTO_TEST_CASE(CrossWideKnownValues)
constexpr int sign(T val)
Definition util.h:166
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< int64_t > VECTOR2L
Definition vector2d.h:709
128-bit integers for exact products of 64-bit coordinate deltas.
constexpr KI_INT128 DotWide(const VECTOR2L &aA, const VECTOR2L &aB)
Exact aA.x * aB.x + aA.y * aB.y.
Definition wide_int.h:113
double ToDouble(KI_INT128 aValue)
Definition wide_int.h:94
constexpr KI_INT128 CrossWide(const VECTOR2L &aA, const VECTOR2L &aB)
Exact aA.x * aB.y - aA.y * aB.x.
Definition wide_int.h:104
__int128 KI_INT128
Definition wide_int.h:91