sanisizer
Sanitize sizes to avoid integer overflow
Loading...
Searching...
No Matches
float.hpp
Go to the documentation of this file.
1#ifndef SANISIZER_FLOAT_HPP
2#define SANISIZER_FLOAT_HPP
3
4#include <limits>
5#include <cmath>
6#include <stdexcept>
7#include <type_traits>
8
9#include "utils.hpp"
10#include "attest.hpp"
11
17namespace sanisizer {
18
22template<typename Integer_, typename Float_>
23bool float_to_int_overflows(Float_ floored_x) {
24 constexpr auto output_precision = std::numeric_limits<Integer_>::digits;
25#ifndef SANISIZER_FLOAT_FORCE_FREXP
26 if constexpr(std::numeric_limits<Float_>::radix == 2) {
27 // ilogb returns an 'exp' such that 2^exp <= floored_x < 2^(exp + 1).
28 return floored_x != 0 && std::ilogb(floored_x) >= output_precision;
29 } else {
30 // Ensure we're covered for weird float types where the radix is not 2.
31 // This is pretty unusual so we need to use a macro to force test coverage.
32#endif
33 int exp;
34 std::frexp(floored_x, &exp);
35 // frexp guarantees that 2^(exp - 1) <= floored_x < 2^exp.
36 return exp > output_precision;
37#ifndef SANISIZER_FLOAT_FORCE_FREXP
38 }
39#endif
40}
57template<typename Integer_, typename Float_>
58Integer_ from_float(Float_ x) {
59 static_assert(std::is_floating_point<Float_>::value);
60 static_assert(std::is_integral<Integer_>::value);
61
62 if (!std::isfinite(x)) {
63 throw std::range_error("invalid conversion of non-finite value in sanisizer::from_float");
64 }
65 if (x < 0) {
66 throw std::out_of_range("negative input value in sanisizer::from_float");
67 }
68
69 x = std::trunc(x);
70 if (float_to_int_overflows<Integer_>(x)) {
71 throw std::overflow_error("overflow detected in sanisizer::from_float");
72 }
73 return x;
74}
75
92template<typename Float_, typename Integer_>
93Float_ to_float(Integer_ x) {
94 const auto val = get_value(x);
95
96 // protect against the various -1 operations.
97 constexpr auto xmax = get_max<I<decltype(x)> >();
98 if constexpr(xmax == 0) {
99 return 0;
100 } else if (val == 0) {
101 return 0;
102 }
103
104 constexpr auto frad = std::numeric_limits<Float_>::radix;
105 constexpr auto fdig = std::numeric_limits<Float_>::digits;
106#ifndef SANISIZER_FLOAT_FORCE_MANUAL
107 if constexpr(frad == 2) {
108 if constexpr(std::numeric_limits<I<decltype(val)> >::digits > fdig) {
109 if constexpr((xmax - 1) >> fdig) {
110 const auto y = (val - 1) >> fdig;
111 if (y) {
112 throw std::overflow_error("overflow detected in sanisizer::to_float");
113 }
114 }
115 }
116 } else {
117#endif
118 // Manual fallback in the unusual case that the radixes is not 2.
119 I<decltype(val)> working = val - 1;
120 for (I<decltype(fdig)> d = 0; d < fdig && working; ++d) {
121 working /= frad;
122 }
123 if (working) {
124 throw std::overflow_error("overflow detected in sanisizer::to_float");
125 }
126#ifndef SANISIZER_FLOAT_FORCE_MANUAL
127 }
128#endif
129
130 return val;
131}
132
133}
134
135#endif
Create compile-time attestations.
Sanitize sizes to avoid integer overflow.
Definition arithmetic.hpp:16
constexpr auto get_value(Value_ x)
Definition attest.hpp:105
Float_ to_float(Integer_ x)
Definition float.hpp:93
Integer_ from_float(Float_ x)
Definition float.hpp:58
constexpr auto get_max()
Definition attest.hpp:119