atof equivalent for long double in standard ANSI C?

L

lcw1964

Greetings again,

I will burden the group with yet another tenderfoot question, but since
conscientious googling hasn't yield a lucid answer I thought I would
risk the shortcut of asking here since I am so very keen to learn to
code in standard C.

Could someone tell me the long double equivalent of atof()? I was
getting some peculiar behaviour in a little bit of math code I am
working with. I thought the problem was in the math algorithms, but not
so--it is the input interface. I get my desired numeric input with
gets(), put it to a character string txt, and put it into a long double
variable x with x = atof(x).

But a little testing as revealed that when the string txt actually
represents a numeral outside of the double range, atof() translates it
not to its long double equivalent, but inf. For example the entered
text string 1e500 is not converted to the long double quantity
1.0E500L, but, as I said, inf.

What is the appropriate ANSI C approach to this? I am aware of
non-standard functions such as atold() or _atold() in other compilers,
but I want to be good boy and speak standard C.

Many thanks for your patient assistance.

Les
 
I

Ivan Vecerina

: Greetings again,
:
: I will burden the group with yet another tenderfoot question, but since
: conscientious googling hasn't yield a lucid answer I thought I would
: risk the shortcut of asking here since I am so very keen to learn to
: code in standard C.
:
: Could someone tell me the long double equivalent of atof()? I was
: getting some peculiar behaviour in a little bit of math code I am
: working with. I thought the problem was in the math algorithms, but not
: so--it is the input interface. I get my desired numeric input with
: gets(), put it to a character string txt, and put it into a long double
: variable x with x = atof(x).

In my draft copy of the C99 standard, there is no long-double
equivalent of atof.
But there is an equivalent of the safer strtod:
long double strtold(const char* nptr, char** endptr);
As this actually provides a safer interface (error-checking),
the omission of the less safe alternative might have been
made on purpose...


hth -Ivan
 
R

Richard Bos

lcw1964 said:
I will burden the group with yet another tenderfoot question, but since
conscientious googling hasn't yield a lucid answer I thought I would
risk the shortcut of asking here since I am so very keen to learn to
code in standard C.

Could someone tell me the long double equivalent of atof()?

There isn't one. atof() is not the best function for the job, anyway,
because it has undefined behaviour on overflow. It is safe _if_ you know
that the string you pass it will always contain a floating point number
that fits in a float. In the general case, though, it's better to use
strtof(), or for long double, strtold(). Unfortunately both of those are
C99 only. C89 has just strtod(), and no function to convert to long
double.
I get my desired numeric input with gets(),

This is a function to avoid under all circumstances. It cannot be told
how many characters to read, so it is impossible to stop it from
scribbling over the end of its buffer - with potentially disastrous
consequences - without the use of OS-specific functions, and for any
colleague who might pass by, leatherware of a kind that is illegal to
sell in several of the States of the USA.

Use fgets() instead.
But a little testing as revealed that when the string txt actually
represents a numeral outside of the double range, atof() translates it
not to its long double equivalent, but inf.

You're lucky. It's allowed to bomb.

Richard
 
K

Keith Thompson

Dann Corbit said:
Use sscanf() instead.
It's a lot better to boot.

Instead of what? Please provide context.

sscanf() invokes undefined behavior if the input string cannot be
represented in the specified type.
 
D

Dann Corbit

Keith Thompson said:
Instead of what? Please provide context.

sscanf() invokes undefined behavior if the input string cannot be
represented in the specified type.

Good point. Maybe something like this could be used as a starting point.
It's not very robust for error checking, but it could easily be improved to
a useful state.

#include <stdlib.h>
#include <float.h>

typedef enum {FALSE=0,TRUE=1} BOOL;

BOOL asciiToLongDouble(const char * const numberString, long double
*pldNumber)
{
/* Convert ASCII string to long double data type. */
long double ldReturnValue;
long double ldOneTenth = 0.1e0L;
long double ldTen = 10.0e0L;
long double ldZero = 0.0e0L;
BOOL bFoundDigits = FALSE;
BOOL bIsNegative = FALSE;
const char * pszNum;

/* Check pointers. */
if (pldNumber == NULL) {
return FALSE;
}
if (numberString == NULL) {
*pldNumber = ldZero;
return FALSE;
}

ldReturnValue = ldZero;

pszNum = numberString;

/* Advance past white space. */
while ((*pszNum == ' ') || (*pszNum == '\t'))
pszNum++;

/* Check for sign. */
if (*pszNum == '+')
pszNum++;
else if (*pszNum == '-')
{
bIsNegative = TRUE;
pszNum++;
}

/* Calculate the integer part. */
while ((*pszNum >= '0') && (*pszNum <= '9'))
{
bFoundDigits = TRUE;
ldReturnValue *= ldTen;
ldReturnValue += *pszNum - '0';
pszNum++;
}

/* Calculate the fractional part. */
if (*pszNum == '.')
{
long double ldScale = ldOneTenth;
pszNum++;
while ((*pszNum >= '0') && (*pszNum <= '9'))
{
bFoundDigits = TRUE;
ldReturnValue += ldScale * (*pszNum - '0');
pszNum++;
ldScale *= ldOneTenth;
}
}

/* If this is not a number, return error condition. */
if (!bFoundDigits)
{
*pldNumber = ldZero;
return FALSE;
}

/* If all digits of integer & fractional part are 0, return 0.0 */
if (ldReturnValue == ldZero)
{
*pldNumber = ldZero;
return TRUE; /* Not an error condition, and no need to continue. */
}

/* Process the exponent (if any) */
if ((*pszNum == 'e') || (*pszNum == 'E'))
{
BOOL bIsNegativeExponent = FALSE;
BOOL bGetOut = FALSE;
long lIndex;
long lExponent = 0;
long double ldTooBigTest = LDBL_MAX*ldOneTenth;
long double ldTooSmallTest= LDBL_MIN*ldTen;

pszNum++;
if (*pszNum == '+')
pszNum++;
else if (*pszNum == '-')
{
pszNum++;
bIsNegativeExponent = TRUE;
}

/* What if the exponent is empty? Return the current result. */
if ((*pszNum < '0') || (*pszNum > '9'))
{
if (bIsNegative)
ldReturnValue = -ldReturnValue;

*pldNumber = ldReturnValue;

return (TRUE);
}
/* Convert char exponent to number <= 2 billion. */
while ((*pszNum >= '0') && (*pszNum <= '9') && (lExponent <
200000000))
{
lExponent *= 10;
lExponent += *pszNum - '0';
pszNum++;
}

/* Compensate for the exponent. */
if (bIsNegativeExponent)
{
for (lIndex = 1; lIndex <= lExponent && !bGetOut; lIndex++)
if (ldReturnValue < ldTooSmallTest)
{
bGetOut = TRUE;
ldReturnValue = LDBL_MIN;
}
else
ldReturnValue *= ldOneTenth;
}
else
for (lIndex = 1; lIndex <= lExponent && !bGetOut; lIndex++)
{
if (ldReturnValue > ldTooBigTest)
{
bGetOut = TRUE;
ldReturnValue = LDBL_MAX;
}
else
ldReturnValue *= ldTen;
}
}

if (bIsNegative)
ldReturnValue = -ldReturnValue;

*pldNumber = ldReturnValue;

return (TRUE);
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top