This question already has answers here:
Working With Floats and Integers
(7 answers)
Closed 8 years ago.
I have this function:
int change(float c)
{
float coins[] = {0.25, 0.10, 0.05, 0.01};
int count[] = {0, 0, 0, 0};
while(c > (0.00))
{
printf("I entered here\n");
if((c-coins[0]) >= (0.00))
{
c -= coins[0];
count[0]++;
printf("-0.25, c = %.2f\n", c);
}
else if((c-coins[1]) >= (0.00))
{
c -= coins[1];
count[1]++;
printf("-0.10, c = %.2f\n", c);
}
else if((c-coins[2]) >= (0.00))
{
c -= coins[2];
count[2]++;
printf("-0.05, c = %.2f\n", c);
}
else if((c-coins[3]) >= (0.00))
{
c -= coins[3];
count[3]++;
printf("-0.01, c = %.2f\n", c);
}
}
printf("(%d) 25 cents, (%d) 10 cents, (%d) 5 cents, (%d) 1 cents\n",
count[0], count[1], count[2], count[3]);
int total = count[0] + count[1] + count[2] + count[3];
return total;
}
And whenever I try to enter 0.06 or 0.07, or any float number that will cause it to reach the last else if clause (the else if((c-coins[3]) >= (0.00))) it will cause an infinite loop.
Whereas if I enter 0.25, 0.10, 0.05 and their respective perfect multiples, the function goes smoothly. (Because maybe it wont reach the last else if clause).
And so when I debugged (using simple printf techniques), I found out that the variable c still enters the while loop even though it has reached 0.00. On some occasions, it would even reach -0.00 or -0.01 (and this only works if I changed the else if((c-coins[3]) >= (0.00)) to else only).
Example (assuming the else if in the code is already else only):
Input: 0.06
c-0.05, c = 0.01
c-0.01, c = -0.00 **<- WTF**
(0) 25 cents, (0) 10 cents, (1) 5 cents, (1) 1 cents
Total Coins: 2
Someone explain this to me? Am I missing out some rules regarding float numbers here? Or some errors are caused by that last else if clause of mine?
Note: Changing the last else if to else may work but can affect the final answer. Also, changing the while condition to -0.00 or 0.001 doesn't work.
Floating point values (typically) use a binary representation. The only fraction in your coins array that can be represented exactly is 0.25. All the other values will be slightly smaller or larger than the correct value. This means that every arithmetic operation on these values, even simple substraction, will introduce small computation errors that will cause your comparisons and output to be faulty.
The best solution, in my opinion, is to use only integer types and perform all your calculations in cents (hundreds). You can print out the amounts using division and modulo.
int change(int cents)
{
int coins[] = {25, 10, 5, 1};
int count[] = {0, 0, 0, 0};
while(cents > 0)
{
if((c-coins[0]) >= 0)
{
c -= coins[0];
count[0]++;
printf("-0.25, c = %d.%02d\n", c/100, c%100);
}
/* and so on */
You may want to use unsigned or long or both depending on your needs.
Related
In some part of my program, I have an array which contains float numbers (for example 9.8, 5.0, 4.45). What I have to do is take those numbers to another array and if they are float (I mean if they have digits after the decimal point that are not zero), then multiply them by 10 enough times to make it an int value (so from the first example, after that I should have 98, 5, 445). I know it doesn't sound clear but it's hard for me to describe my problem better.
My attempt was to do this but I have errors.
for(i=0;i<size;i++)
{
if(teb[i]%10!=0)
{
while(teb[i]%1!=0)
{
teb[i]=teb[i]*10;
}
}
}
I have error: invalid operator to binary % (have float and int) and I am not sure if I can use char or similar variables.
In my opinion, you'll not be able to manage this sanely without using string formatting and then converting from string back to floating-point. I use double rather than float. This is the code I came up with, including your three test values as the first three.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static inline int all_same_as(char *str, char c)
{
while (*str != '\0')
{
if (*str++ != c)
return 0;
}
return 1;
}
static inline void zap_trailing(char *str, char c)
{
char *end = str + strlen(str);
while (end > str)
{
if (*(end - 1) != c)
return;
*--end = '\0';
}
}
static inline void zap_leading(char *str, char c)
{
char *end = str;
while (*end == c)
end++;
if (end > str)
memmove(str, end, strlen(end) + 1); /* Not memcpy()! */
}
int main(void)
{
double d[] =
{
9.8,
5.0,
4.45,
1.0,
18.0,
9.25,
8.719,
2.78128,
2721.0/1001.0,
3.14159,
355.0/113.0,
0.001234, /* Smaller */
1.2345E+13, /* Biggest */
1.2345E-13, /* Smallest */
};
enum { NUM_D = sizeof(d) / sizeof(d[0]) };
for (int i = 0; i < NUM_D; i++)
{
char buffer1[32];
snprintf(buffer1, sizeof(buffer1), "%.6f", d[i]);
char buffer2[32];
strcpy(buffer2, buffer1);
zap_leading(buffer2, ' '); /* Not needed with %.6f; was needed with %10.6f */
char *dot = strchr(buffer2, '.');
if (all_same_as(dot + 1, '0'))
*dot = '\0';
else
{
zap_trailing(dot + 1, '0');
size_t len = strlen(dot + 1);
memmove(dot, dot + 1, len + 1); /* Not memcpy()! */
}
double iv = strtod(buffer2, NULL);
printf("%8g = %8.2g = %8.3g = %10s (converted: %10s - [%s] %g)\n",
d[i], d[i], d[i], buffer1, buffer2, buffer2, iv);
}
return 0;
}
Output:
9.8 = 9.8 = 9.8 = 9.800000 (converted: 98 - [98] 98)
5 = 5 = 5 = 5.000000 (converted: 5 - [5] 5)
4.45 = 4.5 = 4.45 = 4.450000 (converted: 445 - [445] 445)
1 = 1 = 1 = 1.000000 (converted: 1 - [1] 1)
18 = 18 = 18 = 18.000000 (converted: 18 - [18] 18)
9.25 = 9.2 = 9.25 = 9.250000 (converted: 925 - [925] 925)
8.719 = 8.7 = 8.72 = 8.719000 (converted: 8719 - [8719] 8719)
2.78128 = 2.8 = 2.78 = 2.781280 (converted: 278128 - [278128] 278128)
2.71828 = 2.7 = 2.72 = 2.718282 (converted: 2718282 - [2718282] 2.71828e+06)
3.14159 = 3.1 = 3.14 = 3.141590 (converted: 314159 - [314159] 314159)
3.14159 = 3.1 = 3.14 = 3.141593 (converted: 3141593 - [3141593] 3.14159e+06)
0.001234 = 0.0012 = 0.00123 = 0.001234 (converted: 0001234 - [0001234] 1234)
1.2345e+13 = 1.2e+13 = 1.23e+13 = 12345000000000.000000 (converted: 12345000000000 - [12345000000000] 1.2345e+13)
1.2345e-13 = 1.2e-13 = 1.23e-13 = 0.000000 (converted: 0 - [0] 0)
You can make choices about how many decimal digits to support, etc. I chose to use up to 6 after the decimal point (format %.6f used with snprintf()). I included very small (1.23E-13) and very large (1.23E+13) values; the behaviour for even bigger or smaller values is similar.
I initially used %10.6f in the snprintf() statement. When I did that, values were printed with leading blanks. The zap_leading() function removes those. The code has since been revised to use %.6f and there are no leading blanks to zap. I left the code to zap leading characters in place; it could be removed too.
Most decimal fractions expressed as some finite number of digits in base 10 cannot be stored exactly in the binary floating point representations typically used in most implementations.
If you know the numbers are only supposed to be accurate to a fixed number of decimal places n, you can first multiply by 10n and round to the nearest integer. Then strip off up to n trailing zero digits.
For example, for up to 3 decimal places:
for(i=0;i<size;i++)
{
long x = round(teb[i] * 1000.0);
for (int j = 0; j < 3; j++)
{
if (x % 10 == 0)
{
x /= 10;
}
else
{
break;
}
}
teb[i] = x;
}
Normally I don't provide answers to homework problems, but since this is such a bad problem, I'm making an exception.
Before I proceed, let me say that the original problem is meaningless and impossible. Since it's impossible, there's no good way to solve it. So the program I'm going to present has a number of problems, some quite serious.
The core of the program does what the problem asks: As long as the fractional part of one of the numbers is not 0, it multiplies the number by 10. Computing "the fractional part of the number" is performed by an auxiliary function I've written, fractionalpart(). First we'll look at the program, then I'll explain its remaining problems.
#include <stdio.h>
float in[] = {9.8, 5.0, 4.45};
int out[3];
float fractionalpart(float f)
{
return f - (int)f;
}
int main()
{
int i;
for(i = 0; i < 3; i++) {
float tmp = in[i];
while(fractionalpart(tmp) > 0.001) {
tmp = tmp * 10;
}
out[i] = tmp;
}
printf("out:\n");
for(i = 0; i < 3; i++) printf("%d\n", out[i]);
}
The problems this has relate to the fact that, on an ordinary computer using ordinary floating-point representations, there is no such number as 9.8. There's no such number as 4.45, either. What you thought was the float number 4.45, for example, is represented internally as something like 4.44999980926513671875. So a "proper" version of this program would multiply it by 10 a total of twenty times, converting it to 444999980926513671875. But that number won't even fit in a 64-bit integer!
So this program cheats: it doesn't loop until the fractional part is exactly 0; it instead loops until the fractional part is less than 0.001. Where did that number come from? The answer is: nowhere, I pulled it out of the air, it seemed like a good guess. You can experiment with bigger or smaller fudge factors if you like.
The bigger problem -- and you've probably thought of this already -- is that the program assumes that the "fractional part" actually does go down to something like 0.001 or 0.0001. But that's not necessarily true! As we just saw, the number 4.45 is "really" 4.44999980926513671875 inside, so after multiplying by 10 twice, it looks like the program is going to have an integer value of 444 (not 445), and the fractional part is going to be 0.999980926513671875, which is not less than 0.001, so the program is going to keep going!
Actually, it doesn't keep going, it does get the "right answer", at least on my machine (and I think I know why), but there are probably plenty of numbers (roughly half of them, actually) where there will be an intermediate result more like 0.999 than 0.001, and this program will do the wrong thing.
I hate posting code with serious problems like this, it's very tempting to try to write an "improved" version, but it would be much messier, and really, the original question is such a horrible one that it just isn't worth it. (It would be instructive to figure out and explain why this program does seems to work, at least most of the time -- why it doesn't get confused by the occasional 0.999 value -- but I don't have time for that just now, either.)
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
eg : 99999999 to 999,
1382953292 to 138,
12115454.54545 to 121,
9856.12154848 to 985,
Basically, I want to truncate a big number to its first 3 digit without rounding it up.
The input is in 'double'.
How do I ...truncate any ... number to its first 3 digits?
It is easy to get the wrong answer with edges cases. Testing with double values near powers of ten, and just above/below would help shake out weaker solutions.
Converting to a string risks rounding in the textual output, unless the precision used is great enough to prevent rounding of the 3 MS digits. I recommend to start with DBL_DECIMAL_DIG.
With values >= 1.0, no problem
char buf[1+1+1+DBL_DECIMAL_DIG+1+1+6 +1];
// - d . ddzzzzzzzzzzzzz e - eeeeee \0
snprintf(buf, sizeof buf, "%+.*e", DBL_DECIMAL_DIG - 1, some_double);
// Set the least significant digits to 0
memset(buf + 5, DBL_DECIMAL_DIG - 3, '0');
printf("%s %.*e\n", buf, DBL_DECIMAL_DIG - 1, atof(buf));
// To just a 3 digit int
buf[2] = buf[3]; buf[3] = buf[4]; buf[4] = '\0';
printf("%s %d\n", buf, atoi(buf));
For various values < 1.0 and near a power of ten, some issues may remain. Will look into that more later.
There are lot of ways to do it. Some of them would be:
Convert the number to a string, take the first 3 characters and then convert it back to a number.
Divide the number by 10 and capture the result as an integer. Repeat the previous step in a loop until the number becomes less than 1000.
For a range from 0 to 10^15; I'd be tempted to start by finding a multiplier that will convert the number to the range 100 to 999. For example (for a smaller range, untested, incomplete):
if(number >= 10000000000.0) {
multiplier = 100000000.0;
} else if(number >= 1000000000.0) {
multiplier = 10000000.0;
} else if(number >= 100000000.0) {
multiplier = 1000000.0;
} else if(number >= 10000000.0) {
multiplier = 100000.0;
} else if(number >= 1000000.0) {
multiplier = 10000.0;
} else if(number >= 100000.0) {
multiplier = 1000.0;
} else if(number >= 10000.0) {
multiplier = 100.0;
} else if(number >= 1000.0) {
multiplier = 10.0;
} else ....
Once the multiplier is found, you'd use it to convert the number into an integer in the range 100 to 999, like this:
unsigned int result = number * multiplier;
Then you'd print the integer:
printf("%u\n", result);
For performance; finding the multiplier can be done with a kind of binary search to reduce the number of branches executed. For example (for a smaller range, untested, incomplete):
if(number >= 10000.0) {
if(number >= 1000000.0) {
if(number >= 10000000.0) {
multiplier = 100000.0;
} else {
multiplier = 10000.0;
}
} else {
if(number >= 100000.0) {
multiplier = 1000.0;
} else {
multiplier = 100.0;
}
}
} else {
if(number >= 100.0) {
if(number >= 1000.0) {
multiplier = 10.0;
} else {
multiplier = 1.0;
}
} else {
if(number >= 10.0) {
multiplier = ?????
} else {
multiplier = ?????
}
}
}
For numbers that are in the range 0 to 99, you'll have to decide if leading zeros should, or shouldn't, be considered part of "first 3 digits". For example, for the value 0.000000123 you might say that the first 3 digits are 000 (leading zeros are included), and you might say that the first 3 digits are 123 (leading zeros are excluded). This will determine how you choose the multiplier for these numbers and if you have more tests (e.g. if(number >= 0.000000001), etc).
You will also have to make a similar decision for trailing zeros. For example, for the number 5.0 you could say that the result is 500 (trailing zeros included to ensure you always have 3 digits) or you could say that the result is 5 (trailing zeros excluded because there was only one digit).
Depending on these decisions; the code to handle numbers in the range 0 to 99 may become much more complicated.
About Precision
Floating point formats are notoriously bad if you want "guaranteed correct" results due to precision loss that can be introduced by any operation. My approach minimizes the potential for precision loss by ensuring there are only 2 places it can come from (rounding of the multiplier, and the single multiplication); but this does not guarantee the result is correct (e.g. for a large number like 19999999999999.999, the result might be 200).
If you need "guaranteed correct" it becomes extremely difficult. For example, you might be tempted to do an "if(result * multiplier > value)" test in the hope of catching (then fixing) wrong results; but this check would also have potential for precision loss and may do the wrong thing.
In general, my advice is that if you need correct results you shouldn't be using floating point formats - e.g. that it's easier to use "big integers" so that you can avoid precision loss.
This function converts double to three number integer it will not work for 0.0 and some negative numbers.
It takes 3 numbers ignoring zeroes for example: firstThree(-0.0000000001234) will return 123, firstThree(-0.0000000001) will return 100
Maybe I do not understand the idea so be free do DV me as many times as you wish.
int firstThree(double x)
{
double lg;
int ndigits;
if(x < 0) x *= -1;
lg = log10(x);
ndigits = lg > 0 ? lg : round(lg);
if(ndigits > 0)
{
x /= pow(10, ndigits - 2);
}
else
{
if(!ndigits)
{
if(x >= 1.0)
{
x *= 100.0;
}
else
{
x *= 1000.0;
}
}
else
{
x *= pow(10, abs(ndigits));
x *= 100;
}
}
return x;
}
int main(void)
{
printf("%.20f\n", DBL_MAX);
printf("%d\n", firstThree(DBL_MAX));
printf("%.900f\n", DBL_MIN);
printf("%d\n", firstThree(DBL_MIN));
printf("%.30f\n", 99999999999999.0);
printf("%d\n", firstThree(99999999999999.0));
printf("%.10000f\n", 9.999999999999999);
printf("%d\n", firstThree(9.999999999999999));
printf("%.10000f\n", 0.9999999999999999);
printf("%d\n", firstThree(0.9999999999999999));
printf("%.10000f\n", 0.1);
printf("%d\n", firstThree(0.1));
printf("%.1000f\n", 0.00000001999);
printf("%d\n", firstThree(0.00000001999));
}
I recently developed a simple program designed to take a certain amount of money (in dollars) and determine the least number of coins necessary to fulfill that requirement.
#include <stdio.h>
#include <cs50.h>
int main(void)
{
// prompts for change and assigns variable owed_change that value
float owed_change = -1;
while (owed_change < 0 )
{
printf("How much change is owed?\n");
owed_change = GetFloat();
}
// sets the varialble n_coins to 0
int n_coins = 0;
// repeats until no more change is owed
while (owed_change != 0)
{
/* checks for the biggest coin that can be used and increases
the number of coins by 1 */
if (owed_change >= .25)
{
owed_change -= .25;
n_coins++;
}
else if (owed_change >= .10)
{
owed_change -= .10;
n_coins++;
}
else if (owed_change >= .05)
{
owed_change -= .05;
n_coins++;
}
else
{
owed_change -= .01;
n_coins++;
}
}
printf("%d\n", n_coins);
}
The program works for multiples of .25 but runs forever for any other number. From testing, I have found out that it has something to do with the variable owed_change being subtracted from and coming to the result of -0, which satisfies owed_change != 0. However, from research, I have found out that -0 as a floating point should act as +0. If so, what else have I done wrong?
It would be better, in your case, to work with money as cents and multiply all your values by 100. The reason for this is that floats are not exact values; that would be the reason why your code works for floating point values like 0.25, but not for smaller floating point numbers like 0.1, 0.05, and 0.01. For your purpose, you would be better off using an int value.
Instead of:
0.25$, use 25 cents
0.10$, use 10 cents
0.05$, use 5 cents
0.01$, use 1 cent
Change the data type of owed_change to int from float after making the above changes. That should resolve your problem.
So, I am trying to get this C code to work. It compiles, but produces incorrect outputs. It should list all pefect square numbers between 1 and a chosen value.
It is doing something wrong, and after alot of trial and error i think the issue is with the modulus operation...like its truncating early or doing some other strange thing.
// C Code
/*This program will identify all square numbers between one and a chosen integer*/
#include <stdio.h>
#include <math.h>
int main(){
int i, upper, square_int;
float square;
printf("This program will identify all square numbers between one and a chosen integer");
printf("Please enter the upper limit integer:");
scanf("%d", &upper);
upper = 13; /*scanf is the primary integer input method; this is here just to test it on codepad*/
for (i = 1; i<= upper; ++i) /*i want to run through all integers between 1 and the value of upper*/
{
square = sqrt(i); /* calc square root for each value of i */
square_int = square; /* change the root from float to int type*/
if (i % (int)square_int == 0) /*check if i divided by root leaves no remainder*/
printf("%d\n", i); /*print 'em*/
}
printf("This completes the list of perfect squares between 1 and %d",upper);
return 0; /*End program*/
}
The output on codepad is:
This program will identify all square numbers between one and a chosen integerPlease enter the upper limit integer:1
2
3
4
6
8
9
12
This completes the list of perfect squares between 1 and 13
Which is of course wrong. I expect to get 1, 2, 4 and 9 back. Can anyone point out my screw up here?
Here is a simpler algorithm
int i = 1;
while (i*i < upper)
{
printf("%d\n", i*i);
++i;
}
Another method is computing the square root, convert it to int, and compare the numbers.
for (i = 1; i <= upper; ++i)
{
square = sqrt(i);
square_int = square;
if (square == (float)square_int)
printf("%d\n", i );
}
Your modulo operation is incorrect. In case of i = 6 square_int will become 2 and thus i % (int)square_int is equal to 6 % 2 which leads to 0.
You could check if square_int * square_int == i instead.
You says you expect to get 1, 2, 4, 9 which means you don't expect to get 3.
Let's see with i == 3:
sqrt(3) == 1.732051
(int) 1.732051 == 1
3 % 1 == 0.
Which means it actually does what it is expected to do but it will not check if a number is a square.
A simple algorithm to check if the number is a square is to do:
sqrt_int = sqrt(i) + 0.5;
if (square_int * square_int == i)
printf("%d\n", i);
X is a decimal that enters a bunch of loops that keep subtracting certain decimals from it and c is an integer that increments by one every time an operation happens. Four operations should happen but the last loop terminates early and the c never increments from 3 to 4. The last while loop keeps terminating early?
int main(void) {
float x = 0.41;
int c = 0;
while(x >= 0.25)
{
x-= 0.25;
c++;
}
while(x >= 0.1)
{
x-= 0.1;
c++;
}
while(x >= 0.05)
{
x-= 0.05;
c++;
}
while(x >= 0.01)
{
c++;
x-= 0.01;
}
printf("%i", c);
}
Floats in C are not absolutely precise. They have 23 bits to actually represent the actual value. When you write
float x = 0.41
The actual representation of the value in the float value is slightly different from exactly 0.41, depending on your computer architecture/compiler.
To see for yourself, add the following line to each of your loop blocks:
printf("%d, %.30f\n", c, x);
On my machine, I get
1, 0.159999999999999975575093458247
2, 0.059999999999999970023978335121
3, 0.009999999999999967248420773558
Comparisons with floats in C should be handled with epsilon values for these discrepancies. In your case, you should allow the compared values to have be within a bound of the target value. Something like
#include <math.h>
...
while(fabs(x-0.01f) < 0.000001) //check that x is within a certain range of 0.01
Reference on the topic: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
while(x >= 0.05)
{
x-= 0.05;
c++;
}
while(x >= 0.1)
{
c++;
x-= 0.01;
}
try Switching these to these
while(x >= 0.1)
{
c++;
x-= 0.01;
}
while(x >= 0.05)
{
x-= 0.05;
c++;
}
Exact comparisons don't mix with finite precision. It's precisely the same reason if you represent 1/3 with some number of decimal digits (0.3333333), you'll need to add it to zero four times before it's greater than or equal to one.