Skip to content

Commit

Permalink
[DCT-12]: Fix logic in floating point conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
noxpardalis committed Aug 21, 2023
1 parent 3dcb79a commit ca83435
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 6 deletions.
9 changes: 9 additions & 0 deletions src/ddsrt/include/dds/ddsrt/strtod.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ extern "C" {
/**
* @brief Convert a string to a double precision floating point number.
*
* This operation handles locale specific manipulation of the floating point
* string and then sets the output double based on the parsing via `strtod`
* from the standard library. The function returns a failure iff:
* - the string to be parsed represents a value that is too large to store in a double.
* - the string contains junk.
* - the string parses to either `-nan` or `nan`.
* - the string parses to either `-inf` or `inf`.
* It is otherwise successful.
*
* @param[in] nptr A string to convert into a double.
* @param[out] endptr If not NULL, a char* where the address of first invalid
* character is stored.
Expand Down
49 changes: 43 additions & 6 deletions src/ddsrt/src/strtod.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include "dds/ddsrt/io.h"
#include "dds/ddsrt/log.h"
Expand Down Expand Up @@ -133,6 +134,8 @@ ddsrt_strtod(const char *nptr, char **endptr, double *dblptr)
double dbl;
int orig_errno;
dds_return_t ret = DDS_RETCODE_OK;
char *string_end = NULL;
bool successfully_parsed = false;

assert(nptr != NULL);
assert(dblptr != NULL);
Expand All @@ -141,26 +144,60 @@ ddsrt_strtod(const char *nptr, char **endptr, double *dblptr)
if (os_lcNumericGet() == '.') {
errno = 0;
/* The current locale uses '.', so strtod can be used as is. */
dbl = strtod(nptr, endptr);
dbl = strtod(nptr, &string_end);

/* Check that something was parsed */
if (nptr != string_end) {
successfully_parsed = true;
}

/* Set the proper end char when needed. */
if (endptr != NULL) {
*endptr = string_end;
}
} else {
/* The current locale has to be normalized to use '.' for the floating
point string. */
char nptrCopy[DOUBLE_STRING_MAX_LENGTH];
char *nptrCopyEnd = NULL;
delocalize_floating_point_str(nptr, nptrCopy, &nptrCopyEnd);
delocalize_floating_point_str(nptr, nptrCopy, &string_end);

/* Now that we have a copy with the proper locale LC_NUMERIC, we can use
strtod() for the conversion. */
errno = 0;
dbl = strtod(nptrCopy, &nptrCopyEnd);
dbl = strtod(nptrCopy, &string_end);

/* Check that something was parsed */
if (nptrCopy != string_end) {
successfully_parsed = true;
}

/* Calculate the proper end char when needed. */
if (endptr != NULL) {
*endptr = (char*)nptr + (nptrCopyEnd - nptrCopy);
*endptr = (char*)nptr + (string_end - nptrCopy);
}
}

if ((dbl == HUGE_VAL || dbl == 0) && errno == ERANGE) {
// There are two erroring scenarios from `strtod`.
//
// 1. The floating point value to be parsed is too large:
// In this case `strtod` sets `errno` to `ERANGE` and will return a
// parsed value of either `-HUGE_VAL` or `HUGE_VAL` depending on the
// initial sign present in the string.
//
// 2. The string contains a non-numeric prefix:
// In the case junk is passed in `strtod` parses nothing. As a result,
// the value that is returned corresponds to `0.0`. To differentiate
// between the parsing scenarios of junk being passed in versus a valid
// floating point string that parses to 0 (such as "0.0") `strtod` also
// ensures that the provided end pointer == start pointer in the case
// that a junk prefix is encountered.
//
// The two other scenarios that we want to reject are:
//
// 3. The value being parsed results in `-nan` or `nan`.
// 4. The value being parsed results in `-inf` or `inf`.
if (errno == ERANGE || !successfully_parsed
|| isnan(dbl) || isinf(dbl)) {
ret = DDS_RETCODE_OUT_OF_RANGE;
} else {
errno = orig_errno;
Expand Down

0 comments on commit ca83435

Please sign in to comment.