-5

How does (floor) actually work in C? According to techonthenet . com ,

In the C Programming Language, the floor function returns the largest integer that is smaller than or equal to x (ie: rounds downs the nearest integer).

After entering value 4.2 into get_float(), I use floor(4.2 * 100), to turn them into cents and remove decimal places and make it into an integer.

However, I am perplexed why entering the value specifically 4.2 will return a different value for both floor(4.2 * 100), and floor(4.2 * 1000 / 10)? Does this have something to do with imprecision?

See image: When variable entered as 4.2, floor produces different values

Btw, I am totally new to this just started CS50 on edx, and trying out the last exercise of week 1's lesson.. would also love any comments on other parts of the code. More info on the problem here: http://docs.cs50.net/problems/greedy/greedy.html

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
  • *Does this have something to do with imprecision* - yes. – Eugene Sh. Jun 12 '17 at 15:17
  • 1
    Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers. See: How to create a Minimal, Complete, and Verifiable example. – too honest for this site Jun 12 '17 at 15:17
  • Helper exercise: write `1/3` as a decimal point number then multiply the decimal point number you wrote with `3`. Did you get exactly `1`? Don't cheat! Figure out how much is `floor(3*1/3)` using the values you just calculated. A similar thing happens in computers for all numbers that cannot be written as `m/n` with `n` being a positive integral power of `2`. – axiac Jun 12 '17 at 15:17
  • There's a [cs50 stack exchange](http://cs50.stackexchange.com/) if you're interested. – pmg Jun 12 '17 at 16:53

3 Answers3

2

Does this have something to do with imprecision?

It has everything to do with imprecision.

You cannot represent an infinite number of floating point values with a finite number of bits, so what actually gets stored in a floating point type is an approximation for most values. 4.2 cannot be represented exactly in a 32-bit float type; what actually gets stored is something closer to 4.199999809. You need 48 bits in the significand to represent it exactly, meaning you need to use a 64-bit double.

=sigh= I can't add. double gets you a helluva lot closer to 4.2, but not exactly.

And that's just talking about storage, not about rounding and error propagation in arithmetic operations.

In general, it's better to use double instead of float for floating-point work (greater range and precision). Also, it's better to store currency amounts in integral types, scaled to the smallest unit (for example, instead of storing 4.2 dollars, store 420 cents, or 4200 mils). You'll still need to use floating point types for computing interest and things like that, but you'll want to store those results back to an integral type after doing the proper rounding and scaling.

John Bode
  • 119,563
  • 19
  • 122
  • 198
1

float/double cannot represent every possible number exactly. Typical float can represent about 232 different numbers. 4.2 is not one of them. Instead, the nearest value is used. Print using FLT_DECIMAL_DIG to see enough precision of the number to know it is not exactly 4.2

#include <float.h> 
float a = 4.2f;
printf("%.*e\n", FLT_DECIMAL_DIG-1, a);
// 4.19999981e+08

a *= 100;
printf("%.*e\n", FLT_DECIMAL_DIG-1, a);
// 4.19999969e+02

a = floor(a);
printf("%.*e\n", FLT_DECIMAL_DIG-1, a);
// 4.19000000e+02

With a*100 and a*1000 the products incur rounding to the nearest repesentrable answer and recall, a is not exactly 4.2.

a = 4.2;
printf("%.*e\n", FLT_DECIMAL_DIG-1, a * 100);     // 4.19999969e+02
printf("%f\n", floor(a * 100));                   // 419.000000
printf("%.*e\n", FLT_DECIMAL_DIG-1, a * 1000);    // 4.20000000e+03
printf("%.*e\n", FLT_DECIMAL_DIG-1, a * 1000/10); // 4.20000000e+02
printf("%f\n", floor(a * 1000 / 10));             // 420.000000

Using floor() is a poor choice to separate 4.2 into an whole number and fraction when the number needs to be rounded to the nearest 0.01. Instead, scale, round and then separate using matching type functions.

  float a100 = a*100;
  printf("%.*e\n", FLT_DECIMAL_DIG-1, a100);    // 4.19999969e+02
  a100 = roundf(a100);
  printf("%.*e\n", FLT_DECIMAL_DIG-1, a100);    // 4.20000000e+02
  float a100th = fmodf(a100, 100);
  printf("%.*e\n", FLT_DECIMAL_DIG-1, a100th);  // 2.00000000e+01
  a = (a100 - a100th)/100;
  printf("%.*e\n", FLT_DECIMAL_DIG-1, a);       // 4.00000000e+00

Money has many special concerns in C and various approaches have their weaknesses. Using float is a very weak choice.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

Print the value of dollars to see what is actually stored before you try to floor the value.

Jay Buckman
  • 581
  • 5
  • 18