Skip to content

Commit 675885f

Browse files
authored
Merge pull request #194 from snitch-org/cschreib/float-fix
Fix rare invalid output in float to string conversion
2 parents fe9249d + 57c90b4 commit 675885f

File tree

2 files changed

+18
-9
lines changed

2 files changed

+18
-9
lines changed

include/snitch/snitch_append.hpp

+17-9
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,15 @@ constexpr std::size_t min_exp_digits = 2u;
109109
}
110110

111111
[[nodiscard]] constexpr std::size_t num_digits(const signed_fixed_data& x) noexcept {
112+
// Don't forget to modify the stored exponent by the number of stored digits, since we always
113+
// print floating point numbers as 1.23456 but store them as 123456.
114+
// Why +3:
112115
// +1 for fractional separator '.'
113116
// +1 for exponent separator 'e'
114117
// +1 for exponent sign
115-
return num_digits<10>(static_cast<large_uint_t>(x.digits)) + num_exp_digits(x.exponent) +
116-
(x.sign ? 1u : 0u) + 3u;
118+
const std::size_t stored_digits = num_digits<10>(static_cast<large_uint_t>(x.digits));
119+
return stored_digits + (x.sign ? 1u : 0u) +
120+
num_exp_digits(static_cast<fixed_exp_t>(x.exponent + stored_digits - 1)) + 3u;
117121
}
118122

119123
constexpr std::size_t max_float_length = num_digits(signed_fixed_data{
@@ -164,19 +168,20 @@ set_precision(signed_fixed_data fd, std::size_t p) noexcept {
164168

165169
[[nodiscard]] constexpr bool append_constexpr(small_string_span ss, signed_fixed_data fd) noexcept {
166170
// Statically allocate enough space for the biggest float,
167-
// then resize to the length of this particular float.
168171
small_string<max_float_length> tmp;
169-
tmp.resize(num_digits(fd));
170172

171-
const std::size_t exp_digits = num_exp_digits(fd.exponent);
173+
// Resize to fit the digits (without exponent part).
174+
// +1 for fractional separator '.'
175+
// +1 for sign
176+
const std::size_t stored_digits = num_digits<10>(static_cast<large_uint_t>(fd.digits));
177+
tmp.resize(stored_digits + 1u + (fd.sign ? 1u : 0u));
172178

173179
// The exponent has a fixed size, so we can start by writing the main digits.
174180
// We write the digits with always a single digit before the decimal separator,
175181
// and the rest as fractional part. This will require adjusting the value of
176182
// the exponent later.
177-
std::size_t k = 3u + exp_digits;
178-
fixed_exp_t exponent_add = 0;
179-
for (fixed_digits_t j = fd.digits; j != 0u; j /= 10u, ++k, ++exponent_add) {
183+
std::size_t k = 1u;
184+
for (fixed_digits_t j = fd.digits; j != 0u; j /= 10u, ++k) {
180185
if (j < 10u) {
181186
tmp[tmp.size() - k] = '.';
182187
++k;
@@ -191,7 +196,10 @@ set_precision(signed_fixed_data fd, std::size_t p) noexcept {
191196

192197
// Now write the exponent, adjusted for the chosen display (one digit before the decimal
193198
// separator).
194-
const fixed_exp_t exponent = fd.exponent + exponent_add - 1;
199+
const fixed_exp_t exponent = static_cast<fixed_exp_t>(fd.exponent + stored_digits - 1);
200+
201+
// Allocate space for it, +1 for 'e', and +1 for exponent sign.
202+
tmp.grow(num_exp_digits(exponent) + 2u);
195203

196204
k = 1;
197205
for (fixed_exp_t j = exponent > 0 ? exponent : -exponent; j != 0; j /= 10, ++k) {

tests/runtime_tests/string_utility.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ TEST_CASE("append doubles", "[utility]") {
517517
CONSTEXPR_CHECK(a(-2.3456e-320) == ae{"-2.345823686454239e-320"sv, true});
518518
CONSTEXPR_CHECK(a(4.940656458412465e-324) == ae{"4.940656458412465e-324"sv, true});
519519
CONSTEXPR_CHECK(a(-4.940656458412465e-324) == ae{"-4.940656458412465e-324"sv, true});
520+
CONSTEXPR_CHECK(a(-3.479295510743212e-89) == ae{"-3.479295510743212e-89"sv, true});
520521
CONSTEXPR_CHECK(a(std::numeric_limits<double>::infinity()) == ae{"inf"sv, true});
521522
CONSTEXPR_CHECK(a(-std::numeric_limits<double>::infinity()) == ae{"-inf"sv, true});
522523
CONSTEXPR_CHECK(a(std::numeric_limits<double>::quiet_NaN()) == ae{"nan"sv, true});

0 commit comments

Comments
 (0)