shared: add safe_strtofloat()

This is for parsing multiple numbers from one weston.ini key, which
means I cannot use weston_config_section_get_double(). Also going to use
float type, which has less range than double.

Ironically, the tests fill likely fail on locales that do not use
period as the radix character.

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
This commit is contained in:
Pekka Paalanen 2025-07-31 18:03:59 +03:00 committed by Leandro Ribeiro
parent f0be8d94a5
commit c0f79189dd
2 changed files with 84 additions and 0 deletions

View file

@ -73,6 +73,37 @@ safe_strtoint(const char *str, int32_t *value)
return true; return true;
} }
/** Convert a localized string to a single-precision floating point value
*
* \param str The string to parse. Leading whitespace is ignored,
* otherwise the string must be exactly a real number in the current locale.
* \param[out] value Pointer to store the result to on success.
* \return True on success. False on failure and errno set to
* either ERANGE (under- or overflow) or EINVAL (the string is not strictly
* a number).
*/
static inline bool
safe_strtofloat(const char *str, float *value)
{
float ret;
char *end;
assert(str != NULL);
errno = 0;
ret = strtof(str, &end);
if (errno != 0) {
return false;
} else if (end == str || *end != '\0') {
errno = EINVAL;
return false;
}
*value = ret;
return true;
}
/** /**
* Exactly like asprintf(), but sets *str_out to NULL if it fails. * Exactly like asprintf(), but sets *str_out to NULL if it fails.
* *

View file

@ -88,3 +88,56 @@ TEST(strtol_conversions)
return RESULT_OK; return RESULT_OK;
} }
TEST(strtof_conversions)
{
float val;
test_assert_true(safe_strtofloat("0.0", &val));
test_assert_f32_eq(val, 0.0);
test_assert_true(safe_strtofloat("-0.25", &val));
test_assert_f32_eq(val, -0.25);
test_assert_true(safe_strtofloat(" 10", &val));
test_assert_f32_eq(val, 10.0);
test_assert_true(safe_strtofloat("+2.2e-4", &val));
test_assert_f32_eq(val, 2.2e-4);
test_assert_true(safe_strtofloat("3.3e3", &val));
test_assert_f32_eq(val, 3.3e3);
test_assert_true(safe_strtofloat("nan", &val));
test_assert_true(isnan(val));
test_assert_true(safe_strtofloat("inf", &val));
test_assert_f32_eq(val, HUGE_VALF);
test_assert_true(safe_strtofloat("-inf", &val));
test_assert_f32_eq(val, -HUGE_VALF);
test_assert_false(safe_strtofloat("", &val));
test_assert_int_eq(errno, EINVAL);
test_assert_false(safe_strtofloat("x", &val));
test_assert_int_eq(errno, EINVAL);
test_assert_false(safe_strtofloat("15k", &val));
test_assert_int_eq(errno, EINVAL);
test_assert_false(safe_strtofloat("b2.2", &val));
test_assert_int_eq(errno, EINVAL);
test_assert_false(safe_strtofloat("1.3f", &val));
test_assert_int_eq(errno, EINVAL);
test_assert_false(safe_strtofloat("1e-500", &val));
test_assert_int_eq(errno, ERANGE);
test_assert_false(safe_strtofloat("1e+500", &val));
test_assert_int_eq(errno, ERANGE);
return RESULT_OK;
}