Skip to content

Commit a78f904

Browse files
authored
Merge pull request #1783 from evoskuil/master
Expand float to integer casting.
2 parents 70fb6cc + c8362a3 commit a78f904

10 files changed

Lines changed: 552 additions & 290 deletions

File tree

Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ test_libbitcoin_system_test_SOURCES = \
349349
test/math/byteswap.cpp \
350350
test/math/cast.cpp \
351351
test/math/division.cpp \
352+
test/math/expectations.cpp \
352353
test/math/limits.cpp \
353354
test/math/logarithm.cpp \
354355
test/math/multiplication.cpp \

builds/cmake/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,7 @@ if (with-tests)
828828
"../../test/math/byteswap.cpp"
829829
"../../test/math/cast.cpp"
830830
"../../test/math/division.cpp"
831+
"../../test/math/expectations.cpp"
831832
"../../test/math/limits.cpp"
832833
"../../test/math/logarithm.cpp"
833834
"../../test/math/multiplication.cpp"

builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@
275275
</ClCompile>
276276
<ClCompile Include="..\..\..\..\test\math\cast.cpp" />
277277
<ClCompile Include="..\..\..\..\test\math\division.cpp" />
278+
<ClCompile Include="..\..\..\..\test\math\expectations.cpp" />
278279
<ClCompile Include="..\..\..\..\test\math\limits.cpp" />
279280
<ClCompile Include="..\..\..\..\test\math\logarithm.cpp" />
280281
<ClCompile Include="..\..\..\..\test\math\multiplication.cpp" />

builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,9 @@
477477
<ClCompile Include="..\..\..\..\test\math\division.cpp">
478478
<Filter>src\math</Filter>
479479
</ClCompile>
480+
<ClCompile Include="..\..\..\..\test\math\expectations.cpp">
481+
<Filter>src\math</Filter>
482+
</ClCompile>
480483
<ClCompile Include="..\..\..\..\test\math\limits.cpp">
481484
<Filter>src\math</Filter>
482485
</ClCompile>

include/bitcoin/system/constraints.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ using if_little_endian_integral_integer = bool_if<
255255
is_integral_integer<Integer> &&
256256
is_little_endian>;
257257

258+
/// Floating pont types (native, floating point, non-bool).
259+
258260
template <typename Integer>
259261
using if_floating_point = bool_if<
260262
is_floating_point<Integer>>;

include/bitcoin/system/impl/math/cast.ipp

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -209,31 +209,71 @@ constexpr Unsigned to_unsigned(Unsigned value) NOEXCEPT
209209

210210
// Floating point casts.
211211
// ----------------------------------------------------------------------------
212+
// inlines below be not constant until c++23.
212213

213214
template <typename Integer, typename Float,
214215
if_integral_integer<Integer>,
215216
if_floating_point<Float>>
216-
constexpr bool to_integer(Integer& out, Float value) NOEXCEPT
217+
inline bool to_integer(Integer& out, Float value, bool whole) NOEXCEPT
217218
{
218219
if (!std::isfinite(value))
219220
return false;
220221

222+
// TODO: Could use tolerance for fp epsilon to handle representation error.
221223
Float integer{};
222-
const Float fractional = std::modf(value, &integer);
223-
if (fractional != 0.0)
224+
if ((std::modf(value, &integer) != 0.0) && whole)
224225
return false;
225226

226227
if (integer > static_cast<Float>(std::numeric_limits<Integer>::max()) ||
227228
integer < static_cast<Float>(std::numeric_limits<Integer>::min()))
228229
return false;
229230

230-
// Floating point conversion in c++ requires explicit or implicit cast.
231-
BC_PUSH_WARNING(NO_CASTS_FOR_ARITHMETIC_CONVERSION)
232-
out = static_cast<Integer>(integer);
233-
BC_POP_WARNING()
231+
out = to_integer<Integer>(integer);
234232
return true;
235233
}
236234

235+
template <typename Integer, typename Float,
236+
if_integral_integer<Integer>,
237+
if_floating_point<Float>>
238+
inline Integer to_ceilinged_integer(Float value) NOEXCEPT
239+
{
240+
return to_truncated_integer<Integer>(std::ceil(value));
241+
}
242+
243+
template <typename Integer, typename Float,
244+
if_integral_integer<Integer>,
245+
if_floating_point<Float>>
246+
inline Integer to_floored_integer(Float value) NOEXCEPT
247+
{
248+
return to_truncated_integer<Integer>(std::floor(value));
249+
}
250+
251+
template <typename Integer, typename Float,
252+
if_integral_integer<Integer>,
253+
if_floating_point<Float>>
254+
inline Integer to_rounded_integer(Float value) NOEXCEPT
255+
{
256+
return to_truncated_integer<Integer>(std::round(value));
257+
}
258+
259+
template <typename Integer, typename Float,
260+
if_integral_integer<Integer>,
261+
if_floating_point<Float>>
262+
inline Integer to_truncated_integer(Float value) NOEXCEPT
263+
{
264+
if (!std::isfinite(value))
265+
return std::numeric_limits<Integer>::max();
266+
267+
const auto integer = std::trunc(value);
268+
if (integer > static_cast<Float>(std::numeric_limits<Integer>::max()))
269+
return std::numeric_limits<Integer>::max();
270+
271+
if (integer < static_cast<Float>(std::numeric_limits<Integer>::min()))
272+
return std::numeric_limits<Integer>::min();
273+
274+
return to_integer<Integer>(integer);
275+
}
276+
237277
template <typename Integer, typename Float,
238278
if_integral_integer<Integer>,
239279
if_floating_point<Float>>

include/bitcoin/system/math/cast.hpp

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,19 +142,53 @@ constexpr Unsigned to_unsigned(Unsigned value) NOEXCEPT;
142142
/// Floating point casts.
143143
/// ---------------------------------------------------------------------------
144144

145-
/// Cast floating point to integral integer, overflow guarded.
145+
/// Cast floating point to integral integer (truncated).
146+
/// True for any finite value that falls within Integer domain.
147+
/// False if fractional part is non-zero when whole is set true.
146148
template <typename Integer = size_t, typename Float,
147149
if_integral_integer<Integer> = true,
148150
if_floating_point<Float> = true>
149-
constexpr bool to_integer(Integer& out, Float value) NOEXCEPT;
151+
inline bool to_integer(Integer& out, Float value, bool whole=true) NOEXCEPT;
150152

151-
/// Cast floating point to integral integer, overflow unguarded.
153+
/// Cast floating point to integral integer (ceilinged).
154+
/// Any non-finite value returns the integer domain maximum.
155+
/// Overflow/underflow is clamped to respective integer upper/lower limit.
156+
template <typename Integer = size_t, typename Float,
157+
if_integral_integer<Integer> = true,
158+
if_floating_point<Float> = true>
159+
inline Integer to_ceilinged_integer(Float value) NOEXCEPT;
160+
161+
/// Cast floating point to integral integer (floored).
162+
/// Any non-finite value returns the integer domain maximum.
163+
/// Overflow/underflow is clamped to respective integer upper/lower limit.
164+
template <typename Integer = size_t, typename Float,
165+
if_integral_integer<Integer> = true,
166+
if_floating_point<Float> = true>
167+
inline Integer to_floored_integer(Float value) NOEXCEPT;
168+
169+
/// Cast floating point to integral integer (rounded).
170+
/// Any non-finite value returns the integer domain maximum.
171+
/// Overflow/underflow is clamped to respective integer upper/lower limit.
172+
template <typename Integer = size_t, typename Float,
173+
if_integral_integer<Integer> = true,
174+
if_floating_point<Float> = true>
175+
inline Integer to_rounded_integer(Float value) NOEXCEPT;
176+
177+
/// Cast floating point to integral integer (truncated).
178+
/// Any non-finite value returns the integer domain maximum.
179+
/// Overflow/underflow is clamped to respective integer upper/lower limit.
180+
template <typename Integer = size_t, typename Float,
181+
if_integral_integer<Integer> = true,
182+
if_floating_point<Float> = true>
183+
inline Integer to_truncated_integer(Float value) NOEXCEPT;
184+
185+
/// Cast floating point to integral integer, overflow/underflow UNGUARDED.
152186
template <typename Integer = size_t, typename Float,
153187
if_integral_integer<Integer> = true,
154188
if_floating_point<Float> = true>
155189
constexpr Integer to_integer(Float value) NOEXCEPT;
156190

157-
/// Cast integral integer to floating point, overflow unguarded.
191+
/// Cast integral integer to floating point, overflow/underflow not possible.
158192
template <typename Float = double, typename Integer,
159193
if_floating_point<Float> = true,
160194
if_integral_integer<Integer> = true>

test/math/addition.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919
#include "../test.hpp"
2020

21-
// creates addition_tests namespace
21+
// creates addition_tests namespace
2222
BOOST_AUTO_TEST_SUITE(addition_tests)
2323

2424
constexpr uint32_t pos_uint32 = 42;

0 commit comments

Comments
 (0)