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#include <cassert>
9
10#include "utils.hpp"
11#include "attest.hpp"
12
18namespace sanisizer {
19
30template<typename Float_>
32 static_assert(std::is_floating_point<Float_>::value);
33 assert(x >= 0);
34 assert(std::isfinite(x));
35 assert(std::trunc(x) == x);
36
37#ifndef SANISIZER_FLOAT_FORCE_FREXP
38 if constexpr(std::numeric_limits<Float_>::radix == 2) {
39 if (x == 0) {
40 return 0;
41 } else {
42 // ilogb returns an 'exp' such that 2^exp <= x < 2^(exp + 1).
43 return std::ilogb(x) + 1;
44 }
45 } else {
46#endif
47 // Ensure we're covered for weird float types where the radix is not 2.
48 int exp;
49 std::frexp(x, &exp);
50 // frexp guarantees that 2^(exp - 1) <= x < 2^exp.
51 return exp;
52#ifndef SANISIZER_FLOAT_FORCE_FREXP
53 }
54#endif
55}
56
69template<typename Integer_, typename Float_>
70Integer_ from_float(Float_ x) {
71 static_assert(std::is_floating_point<Float_>::value);
72 static_assert(std::is_integral<Integer_>::value);
73
74 if (!std::isfinite(x)) {
75 throw std::range_error("invalid conversion of non-finite value in sanisizer::from_float");
76 }
77 if (x < 0) {
78 throw std::out_of_range("negative input value in sanisizer::from_float");
79 }
80 x = std::trunc(x);
81
82 constexpr auto output_precision = std::numeric_limits<Integer_>::digits;
83 if (required_bits_for_float(x) > output_precision) {
84 throw std::overflow_error("overflow detected in sanisizer::from_float");
85 }
86
87 return x;
88}
89
106template<typename Float_, typename Integer_>
107Float_ to_float(Integer_ x) {
108 const auto val = get_value(x);
109
110 // protect against the various -1 operations.
111 constexpr auto xmax = get_max<I<decltype(x)> >();
112 if constexpr(xmax == 0) {
113 return 0;
114 } else if (val == 0) {
115 return 0;
116 }
117
118 constexpr auto frad = std::numeric_limits<Float_>::radix;
119 constexpr auto fdig = std::numeric_limits<Float_>::digits;
120#ifndef SANISIZER_FLOAT_FORCE_MANUAL
121 if constexpr(frad == 2) {
122 if constexpr(std::numeric_limits<I<decltype(val)> >::digits > fdig) {
123 if constexpr((xmax - 1) >> fdig) {
124 const auto y = (val - 1) >> fdig;
125 if (y) {
126 throw std::overflow_error("overflow detected in sanisizer::to_float");
127 }
128 }
129 }
130 } else {
131#endif
132 // Manual fallback in the unusual case that the radixes is not 2.
133 I<decltype(val)> working = val - 1;
134 for (I<decltype(fdig)> d = 0; d < fdig && working; ++d) {
135 working /= frad;
136 }
137 if (working) {
138 throw std::overflow_error("overflow detected in sanisizer::to_float");
139 }
140#ifndef SANISIZER_FLOAT_FORCE_MANUAL
141 }
142#endif
143
144 return val;
145}
146
147}
148
149#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:107
Integer_ from_float(Float_ x)
Definition float.hpp:70
int required_bits_for_float(Float_ x)
Definition float.hpp:31
constexpr auto get_max()
Definition attest.hpp:119