C's strtod() returns wrong values - c

I'm currently working on a banking terminal program for the laboratory exercises at my university.
What puts me off my stride is a function supposed to take the user's input of amount to be transfered, check if it fits all requirements and if so, returns the value provided to the program.
Our tutor is mad about all means of securing the input, and so I have to call an error on any kind of inconsistency. Value too high? Error. Negative or zero value? Error. A number more precise than 0.01? Error. Non-digit-non-dot characters in the input? Error.
Due to that, my function is definately overcomplicated, yet I'm fine with that. What drives me up the wall is the fact, that both atof() and strtod() functions reads numbers in a somehow wrong way.
long double take_amount()
{
char input[21];
bool success, correct;
unsigned long int control1;
long double amount, control, control2, control3;
do
{
success = true, correct = true;
printf("\t\tAmount:\t\t");
success = scanf("%20[^ \t\n]c", input);
__fpurge(stdin);
correct = check(input, 'b');
amount = strtod(input, NULL);
printf("\n\tGOT %.20Lf\n", amount); ///
control = amount * 100;
control1 = (unsigned long int)floor(control);
control2 = (long double) control1;
control3 = control2 - control;
printf("\n\tGOT2 %.20Lf\n", control3); ///
if (control3 != 0)
{
if (control3 >= 1 || control3 <= -1)
{
printf("\n\t\tWe are sorry, but for the safety reasons it is impossible to transfer");
printf("\n\t\tsuch a great amounts while online. If you really wish to finalize");
printf("\n\t\tthis operation, please, visit the closest division of our bank.");
printf("\n\t\tWe are sory for the inconvenience and wish you a pleasent day.");
press_enter();
}
correct = false;
printf("\nDAMN\n"); ///
}
if (amount <= 0)
{
correct = false;
printf("\nGOD DAMN\n"); ///
}
if(success == false || correct == false)
{
printf("\n\t\tInvalid input, please try again.\n\n");
}
else
{
printf("\t\t\t\t%.2Lf\n", amount);
printf("\n\t\tIs it correct input? ((Y)es/(N)o/(E)xit)");
if (accept())
{
break;
}
else
{
continue;
}
break;
}
}while (1);
return amount;
As far as my functions used here goes, check() checks if the string contains only legal characters (digits and dot), press_enter() waits for enter-press to leave to main menu and accept() reads only y/n/e, return true on y, false on n and leaves to menu on e.
The lengthy part with control variables is my solution for checking if the number is not more precise than 0.01. Sadly, it doesn't work due to strtod().
My problem is that strtod() doesn't really work! Even with really medicore numbers, being far from underflow or overflow, the value returned doesn't match the input. Some examples:
Enter the amount you would like to deposit.
Amount: 1234.45
GOT 1234.45000000000004547474
Enter the amount you would like to deposit.
Amount: 0.999
GOT 0.99899999999999999911
It is not unlikely that it's my fault, but after several hours with this code I still couldn't come up with a working solution and that's why I'm asking for you help, vise internauts of Stack Overflow.
Is there any way to fix the reading of strtod()? If not, is there another way to take that input that let's me check all that needs to be checked?
EDIT: ATTENTION, PLEASE!
If I haven't stated already, that my tutor is not the easiest guy to work with, I do so now.
He FORCES us to use DOUBLE format to hold the BALANCE stored on accounts. I know it because one of my collegues got his program REJECTED for using two-int, dollar - cent construction.
I highly appreciate you help here, so can you somehow solve that problem? I HAVE TO use double type to store the money. I also HAVE to check if there were no letters in the input (scanf() set on %Lf will just cut all non-digits from the end), HAVE TO check if the input is not more precise then 0.01, HAVE TO accept xxx.xx structure of the input. Any suggestions?

Use strtol to read the dollars and again (or just your own trivial function) to read the cents, then combine them as cents += 100*dollars; Do not use floating point for currency. Ever.

IEEE floating point numbers are meant to hold approximate values over a huge range, not precise values. They cannot represent some very simple values precisely, such as 0.1, but they can represent 1.0, 0.5, 0.25, 0.125, ... precisely. They can also represent 2.0, 4.0, 8.0, 16.0, ... precisely. How far these series go is related to how many bits the floating point value is represented by. You should have noticed that all of the examples values that I've given are powers of 2, and from that you should see that floating point values can also be made up of sums of powers of 2, which is mostly correct. Summing the values of 2 works well for representing whole numbers as the entire range from least to greatest can be represented by integer types, but with fractional values things are different. There are many values between the values that can be represented that are not expressible for a given floating point type (32, 64, 80, or 128 bit floating point variables), so when these are read in by C library functions (or by the compiler from your source code) there are rounding rules that come into play. A very similar thing happens when the numbers are written back out by a C program.
When you need to deal with precise values you must be aware of this. For money, which usually needs this type of precision, you will want to convert it to a type that can represent this type of precision. In many languages there are separate types for this, but in C you get to put the values into integer types or structs made up of integers.

Divide and Conquer
Separate user input from data validation.
User input:
Current code uses input without first validating success
success = scanf("%20[^ \t\n]c", input);
correct = check(input, 'b');
amount = strtod(input, NULL); // Bad: `input` not known to have been successfully scanned.
Instead:
char input[40]; // no need for tight buffer size
if (fgets(input, sizeof input, stdin) == NULL) Handle_EOF();
// remove potential trailing \n
input[strcspn(input, "\n")] = 0;
Now start assessing if input is a valid with various tests
char *endptr;
errno = 0;
double value = strtod(input, &endptr);
if (input == endptr) Handle_NothingScanned();
if (*endptr != '\0') Handle_GarbageAtEndOfInput();
// Use 1 of the 2: First allows input like 1e-1000 which underflows to 0.0
if (errno == ERANGE && fabs(value) > DBL_TRUE_MIN) Handle_Overflow();
if (errno == ERANGE) Handle_OverUnderflow();
// work with numbers in a reasonable range of 'double'
double max_range = pow(10.0, DBL_DIG - 2);
if (!(value >= -max_range && value <= max_range)) Handle_InfinityNotanumberOverflow();
// Test insures that the number entered is the closest possible `double` to xxxxxx.xx
// Method 1: Math
double rvalue = round(value*100.0)/100.0;
if (rvalue != value) Handle_NumberMorePreciseThan_0_01();
// Method 2: round trip: double --> string --> double
char buffer[DBL_DIG + 10];
sprintf(buffer, "%.2f", value);
if (value != strtod(buffer, 0)) Handle_NumberMorePreciseThan_0_01();
// Insure xxxx.xx format
if ((endptr - input) < 3 || endptr[-3] != '.') Handle_BadFormat();
GoodToUse(value);

This is a known "issue" with respect to how numbers are represented in floating point. The representation of 1234.45 in floating point IS what is being entered.
This is due to the fact that when translated into the floating point representation the actual precise string of bits is longer than the space used to store it (when dealing with a number that is not a power of two there is always a chance of this occurring.)
Another option that you can do is create a 'Currency' struct that holds two ints (the dollar part and the fractional part) and pass it around. You'll still need to handle the code for splitting the currency string into those parts though.

If the requirement to make use of a double number is strict then another property of floating point numbers may be of use: An arbitrary integer value stored in a floating point variable will be more likely to be precise than a number with a fractional component.
This fact leads me to ask: Have you thought of storing your currency values such that $1234.45 is 123445?

Related

C - Float Imprecision, How to lock the decimal places for calculations?

I was making a little program to test floats in the C: The program itself is very simple, I just want to based on the User's input, return how much Dollar(s), Quarter(s)... etc, his number has.
//------------------------------> First Part: All the necessary Variables <-----------------------------
int main (void)
{
//Getting the user Input
float number = get_float("Number: ");
//Checking if is a positive number
if (number < 0)
{
printf("A positive number, Please: ");
}
//Declaring my Constant/Temporary variables.
float coinValues[] = {1.00, 0.25, 0.10, 0.5, 0.01};
char *coinNames[] = {"Dollar(s): ", "Quarter(s): ", "Dime(s): ", "Nickel(s): ", "Penny(ies): "};
int i = 0;
int tmp = 0;
//-----------------------------------> Second Part: The code Itself <-----------------------------------
//Checking/Printing the necessary coins.
while (number > 0)
{
//Until the loop stops, check if the number can be divided by the CoinValue.
if (number >= coinValues[i])
{
//Print the current Coin Name from the divided value.
printf("%s", coinNames[i]);
//Check if the Current Number still contains Coin Values inside of it, if True counts one in your "Coin Score".
while (number >= coinValues[i])
{
number -= coinValues[i];
tmp++;
}
//Print the Current "Coin Score", then resets TMP.
printf("%i\n", tmp);
tmp = 0;
}
else
{
//Updating the Coin value
i++;
}
}
}
My program was running very well as long I use Integers, but when I converted this code for Floats the values (Dime(s), Nickel(s), and Penny(ies)) start to return non-expected results in the Int variable tmp.
An expected result for a number like 2.6, will be 2 Dollars, 2 Quarters, and 1 Dime, but sometimes, instead of use the Dime(s), the program skips them entire and make the operation with the Nickel(s), however, what is bugging me is that the program is always returning AWL=+ without any value and then the program stay froze forever.
Considering that my only thought is that I'm "suffering" from Float Imprecision, and I don't know how to solve it, so can anyone Help me?
Ps. The program needs to always return the maximum value from each coin before pass forward.
Floating-point arithmetic is designed to approximate real-number arithmetic. IEEE 754-2008 says “Floating-point arithmetic is a systematic approximation of real arithmetic…” So there is no way to “lock” decimal digits in binary floating-point. It is intended to be used where you want an approximation, such as modeling physics. That even includes some financial applications, such as options evaluation. Although floating-point arithmetic can be used for exact calculations in some situations, these require particular care and are generally only pursued in special circumstances.
So the answer to your question on how to lock decimal places is there is no good way to do that with binary floating-point. Attempting to do so generally just yields the same effects as integer arithmetic but less efficiently and with more difficult code. So use integer arithmetic and scale the amounts according to the smallest unit of currency desired, such as a penny.

Create a precise atof() implementation in c

I have written an atof() implementation in c . I am facing rounding off errors in this implementation . So , putting in a test value of 1236.965 gives a result of 1236.964966 but the library atof() function reurns 1236.965000 . My question is , how to make the user defined atof() implementation more 'correct' ?
Can the library definition of atof() be found somewhere ?
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
float str_to_float(char *);
void float_to_str(float,char *);
int main(){
int max_size;
float x;
char *arr;
printf("Enter max size of string : ");
scanf("%d",&max_size);
arr=malloc((max_size+1)*sizeof(char));
scanf("%s",arr);
x=str_to_float(arr);
printf("%f\n%f",x,atof(arr));
return 0;
}
float str_to_float(char *arr){
int i,j,flag;
float val;
char c;
i=0;
j=0;
val=0;
flag=0;
while ((c = *(arr+i))!='\0'){
// if ((c<'0')||(c>'9')) return 0;
if (c!='.'){
val =(val*10)+(c-'0');
if (flag == 1){
--j;
}
}
if (c=='.'){ if (flag == 1) return 0; flag=1;}
++i;
}
val = val*pow(10,j);
return val;
}
Change all your floats to doubles. When I tested it, that gave the same result as the library function atof for your test case.
atof returns double, not float. Remember that it actually is double and not float that is the "normal" floating-point type in C. A floating-point literal, such as 3.14, is of type double, and library functions such as sin, log and (the perhaps deceptively named) atof work with doubles.
It will still not be "precise", though. The closest you can get to 1236.965 as a float is (exactly) 1236.9649658203125, and as a double 1236.964999999999918145476840436458587646484375, which will be rounded to 1236.965000 by printf. No matter how many bits you have in a binary floating-point number, 1236.965 can't be exactly represented, similar to how 1/3 can't be exactly represented with a finite number of decimal digits: 0.3333333333333333...
And also, as seen in the discussion in comments, this is a hard problem, with many possible pitfalls if you want code that will always give the closest value.
I used your code as inspiration to write my own.
What other commenters and answers do not recognize is that the original reason for the question is an embedded situation. In my case the library "atof" pulls in something that does "printf" which pulls in "systemcalls" which I don't have.
So.... here I present a simple (does not implement exponential notation) atof implementation that works in floats, and is suitable for embedding.
My implementation uses way less variables.
float ratof(char *arr)
{
float val = 0;
int afterdot=0;
float scale=1;
int neg = 0;
if (*arr == '-') {
arr++;
neg = 1;
}
while (*arr) {
if (afterdot) {
scale = scale/10;
val = val + (*arr-'0')*scale;
} else {
if (*arr == '.')
afterdot++;
else
val = val * 10.0 + (*arr - '0');
}
arr++;
}
if(neg) return -val;
else return val;
}
how to make the user defined atof() implementation more 'correct' ?
Easy: 1) never overflow intermediate calculation and 2) only round once (at the end).
It is hard to do those 2 steps.
Note: C's atof(), strtof(), etc. also handle exponential notation - in decimal and hex.
Potential roundings
val*10
(val*10)+(c-'0');
pow(10,j)
val*pow(10,j) // This last multiplication is the only tolerable one.
Potential overflow (even though the final answer is within range)
val*10
(val*10)+(c-'0');
pow(10,j)
Using a wider type like double can greatly lessen the occurrence of such problems and achieve OP's "more 'correct'". Yet they still exist.
This is not an easy problem to solved to get the best (correct) floating point result from all string inputs.
Sample approaches to solve.
Avoid overflow: rather than pow(10,j):
val = val*pow(5,j); // rounds, `pow(5,j)` not expected to overflow a finite final result.
val = val*pow(2,j); // Does not round except at extremes
Code should form (ival*10)+(c-'0') using extended integer math in the loop for exactness.
Yet this is just scratching the surface of the many corner cases.
#Eric Postpischil commented on a robust C++ code that handles non-exponential notation string input well. It does initial math using integers and only rounds later in the process. This linked code is not visible unless your rep is 10,000+ as the question was deleted.

Newton Raphson iteration trapped in infinite loop

I'm quite a beginner in this topic, and couldn't find out the reason: sometimes the program works, sometimes not (after asking the question, it simply doensn't want to take in my answers, than I can write in as much as I want, it doesn't respond, just list out the numbers, I tiped in)
#include <stdio.h>
float abszolut (float szam)
{
float abszoluterteke;
if (szam >=0)
abszoluterteke = szam;
else
abszoluterteke = -szam;
return abszoluterteke;
}
float negyzetgyok (float szam)
{
float pontossag = 0.000001;
float tipp = 1;
if (szam <0)
{
printf ("Megszakítás elfogadva! \nKöszönjük, hogy programunkat választotta!\n");
return -1;
}
else
{while (abszolut (tipp*tipp-szam) >= pontossag)
tipp = (szam/tipp + tipp)/2;
return tipp;
}
}
int main (void)
{
float alap, eredmeny;
for (;;)
{
printf ("Melyik számnak szeretnéd meghatározni a négyzetgyökét ilyen módszerrel?\n");
scanf ("%f", &alap);
eredmeny = negyzetgyok (alap);
if (eredmeny == -1)
return 1;
else
printf ("A(z) %f négyzetgyöke megfelelő közelítéssel: %f\n", alap, eredmeny);
}
return 0;
}
Change for abszolut (tipp*tipp-szam) >= pontossag*szam
The while loop must stop once tipp*tipp is close to szam. But IEEE floating point computations have a limited precision : about 7 digits for float and 15 digits for double.
So the error on float szam is about 0.0000001*szam. It's the same for tipp. Consequently, the error on tipp*tipp-szam is higher than 0.0000001*szam. If szam is large, this error will hardly become lower than 0.000001. Even if double precision is used, it is likely that while (abszolut (tipp*tipp-szam) >= pontossag) triggers an infinite loop for very large numbers.
On the other side, what happens if szam is very small, say 1e-10 ? The while loop prematurely exits and the square root of 1e-10 is computed as something about 1e-3, instead of 1e-5... The relative error is about 10000%... And using double does not change anything !
To avoid this, you can change for abszolut (tipp*tipp-szam) >= pontossag*szam.
Notice that both sides have the same dimension. If szam were in square feet, tipp would be in feet and pontossag, the precision, is dimensionless. It is a good practice to compare things having the same dimension.
If you keep noticing infinite loops, switch to double precision or increase pontossag.
To avoid infinite loop, add a counter int i; and exit the while loop if the number of iteration is 100. 100 should be sufficient, since your Newton-Raphson iteration features a quadratic convergence.
There are a number of problems with your code.
The exit condition in your loop is flawed.
The problem with your square root algorithm is the use of the error limit pontossag. Your algorithm will give erroneous results for very small numbers and it will loop forever for numbers larger than 20 or so. To fix this, change the loop test from abszolut (tipp*tipp-szam) >= pontossag to abszolut (tipp*tipp-szam) >= pontossag*szam.
You aren't checking for all the problem cases.
If your computer uses IEEE 754 floating point, your algorithm happens to work. That's just luck. Never rely on luck when doing numerical programming. It's easy to input an infinity. For example, 3.5e38 (350000000000000000000000000000000000000) does the trick with single precision numbers (float). Your function negyzetgyok should check for infinity:
if (isinf (szam))
{
return szam;
}
You can do much better than an initial guess of 1.0 as the square root.
An initial guess of 1.0 against 3.4e38 means a lot of needless looping. A fast and easy way to form a good initial guess is to take advantage of the fact that floating point numbers are represented internally as (1+fractional_part)*2^exponent. A good first guess is 1*2^(exponent/2). With single precision numbers,
int expo;
float tipp;
frexpf (szam, &expo);
tipp = ldexpf (1.0f, n/2);
You are using %f rather than %g to parse floating point numbers.
The %g format can parse anything that can be parsed with the %f format, plus a whole lot more.
You aren't checking the status of fscanf.
Enter x when prompted to enter a number. The scanner will read that character, which stops the scan. The scanner will put that character (x) back into the input buffer, and return 0, indicating that nothing was scanned. The next time around, the scanner will read the character x again, once again put that character back into the input buffer, and once again return 0. Infinite loop! Always check the status of any of the scanf family of functions to see if the scanner scanned the number of items expected.
You are using fscanf.
There are a number of existing questions and answers at this site that address the many problems with using fscanf to read from a file. This is particularly so when reading human-generated input. People make mistakes. Ignoring that people do make mistakes on entering data is a programming error. A better approach would be to read a line into a buffer using frets and parse that line with sscanf.

Casting an int into a float results in an incorrect value (not the usual decimal rounding error though)

For some reason I am getting some very unexpected results when converting an integer in to a float (using c(-ish) on Arduino).
In the past I have had problems with memory on the arduino so that may be either a helpful troubleshooting hint or a red herring.... you're pick.
Anyways, here is my code (only the relevant parts), and the value that is given is "6/8" which should result in meterValue = 0.75:
My function:
int getIntegerFromFileStream(File f, char* previewedChar) {
int num = 0;
while (48 <= *previewedChar && *previewedChar <= 57) {
// Ascii range 48-57 represents digits 0-9
num = (num*10) + atoi(previewedChar);
*previewedChar = f.read();
}
Serial.print(F("Found integer: "));
Serial.println(num);
}
Main code:
[.... rest of my code ....]
// Get the value for the meter from the fractional form (ex: 4/4 or 6/8)
meterValue = (float)getIntegerFromFileStream(file, &inputChar);
Serial.print(F("New meter value: "));
Serial.println(meterValue);
if (inputChar == '/') {
inputChar = file.read(); // move past the '/'
meterValue /= (float)getIntegerFromFileStream(file, &inputChar);
Serial.print(F("New meter value: "));
Serial.println(meterValue);
}
[.... rest of my code ....]
And here is the output from the above code:
Found integer: 6
New meter value: 3.00
Found integer: 8
New meter value: 1.00
New meter value: 1.00
As you can see from the output, the integer itself is being parsed correctly, but once it leaves the function and is casted to a float, its value changes (and not in the usual float-decimal-error way).
Any tips would be appreciated!
Edit:
Found my newb mistake: i don't have a return value for my getInt function, so it was resulting in undefined behaviour.
Turns out that, by default, Arduino does not give warnings during compilation unless you specifically go in to Preferences and turn on 'verbose' output for compilation. Once that was set I was able to see these kinds of warnings.
(this is only edited in for now because I'm too new to stackoverflow to answer my own question yet, i'll do so once my time limit is up)
Found my newb mistake: i don't have a return value for my getInt function, so it was resulting in undefined behaviour.
Turns out that, by default, Arduino does not give warnings during compilation unless you specifically go in to Preferences and turn on 'verbose' output for compilation. Once that was set I was able to see these kinds of warnings.

convert char* to double with max 2 places after the point

i'm reading tokens from file, it enters the char pointers perfectly as it comes
in the file, but after sending it to this function:
double CharToDouble(char *ptemp)
{
if(ptemp != NULL)
{
if(ptemp[0] == '0')
return atof(ptemp);
else
return atof(ptemp)/100;
}
return 0;
}
the values i'm getting in the doubles where i save this function result are like 0.6600000000001
0.280000000000000000
and stuff like that, i want it to be ecaxtly as in the char*..
it's money issues, in cents.
any idea?
If it's a currency, multiply by 100 and round down to an integer, so instead of 123.45, you have 12345.
Note: Float and Double are accurate only up to a certain precision (machine precision), because not every real number can be encoded in the floating point format.
When you're only interested in output of the correct format, you must use the correct printf command, i.e.
double currency_value = 9.95;
printf("Currency: %.2f", currency_value)
Look up "Format String" to learn more. The %.2f says that I want a floating point number with a fixed position after the comma (f) and that this position should be the second number after the comma (2).

Resources