c4core icon indicating copy to clipboard operation
c4core copied to clipboard

Should `atod` fail when different from `inf` or `infinity`

Open captain-yoshi opened this issue 3 years ago • 1 comments

A string that begins with inf, regardless of what comes after (except "" and "inity"), will silently succeed :

double d;
char str[] = "Informed"

if (atod(str, &d)) {
    std::cout << "This should fail !" << std::endl;
    std::cout << d << std::endl;
}

// This should fail !
// inf

Does atod do whole string matching ? Is this the intended behavior.


My use case is for decoding primitives not known in advance in a specific order :

    {  // bool
        bool value;
        if (from_chars(substr, &value))
            return XmlRpc::XmlRpcValue(value);
    }

    {  // int
        int value;
        if (from_chars(substr, &value))
            return XmlRpc::XmlRpcValue(value);
    }

    {  // double
        double value;
        if (from_chars(substr, &value))
            return XmlRpc::XmlRpcValue(value);
    }

    {  // string
        std::string value;
        if (from_chars(substr, &value))
            return XmlRpc::XmlRpcValue(value);
    }

Ultimately, is it up to me to check for a inf character is valid ?

captain-yoshi avatar Dec 28 '22 20:12 captain-yoshi

So adding this patch, works as expected for my use case (Based on https://github.com/fastfloat/fast_float/issues/164). We would need to check that the string has been fully consumed by fastfloat. Some other functions would maybe need this change :

  • atod -> C4CORE_HAVE_STD_FROMCHARS
  • atof
  • other ?
C4_ALWAYS_INLINE bool atod(csubstr str, double * C4_RESTRICT v) noexcept
{
    C4_ASSERT(str.triml(" \r\t\n").len == str.len);
#if C4CORE_HAVE_FAST_FLOAT
    // fastfloat cannot parse hexadecimal floats
    bool isneg = (str.str[0] == '-');
    csubstr rem = str.sub(isneg || str.str[0] == '+');
    if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
    {
        fast_float::from_chars_result result;
        result = fast_float::from_chars(str.str, str.str + str.len, *v);

        // str must be fully consumed to be valid
        if(result.ptr - str.str != str.len)
            result.ec = std::errc::invalid_argument;
        
        return result.ec == std::errc();
    }
    else if(detail::scan_rhex(rem.sub(2), v))
    {
        *v *= isneg ? -1. : 1.;
        return true;
    }
    return false;
#elif C4CORE_HAVE_STD_FROMCHARS
    std::from_chars_result result;
    result = std::from_chars(str.str, str.str + str.len, *v);
    return result.ec == std::errc();
#else
    csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+');
    if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
        return detail::scan_one(str, "lf", v) != csubstr::npos;
    else
        return detail::scan_one(str, "la", v) != csubstr::npos;
#endif
}

captain-yoshi avatar Dec 29 '22 04:12 captain-yoshi