1

I've looked at this question, and it doesn't appear to be the same situation.

I have a function in the check register program I'm writing that reads in a dollar amount from the user, validates it wasn't entered with fractional cents, and checks with the user that it was entered correctly. What I'm getting, though, is that after multiplying the float value that was entered by 100 and truncating with (int), the value changes. For instance, if I enter 1118.58, I can (via debug printf statements) verify that 1118.58 was scanned and assigned to amt correctly, and my while conditional (int)100 * amt == 100 * amt is correctly testing as TRUE, but after conversion to integer, the integer value is stored as 111857. More perplexing, it doesn't happen with every value (though I'm just getting going well on testing, this is the first entry I've seen change this way).

I'm using gcc on Kubuntu 14.04 64-bit, with C99 standard setting in my compile command. Here's the function that's giving the problem (presuming you don't want/need to see 1300 lines of the complete program). Note: I have Boolean values and operators defined in a header file for my convenience -- I've recently discovered there's a standard way to do that, but haven't converted my header file yet.

int get_amt (void)
{
  float amt;
  int scanned, i_amt;
  int success = FALSE;
  char yn = '\0';

  while (NOT success)
  {
    scanned = scanf("%f%*c", &amt);

    /* validate and verify amount */
    if (scanned >= 1 AND (int)100*amt == 100*amt AND amt <= 100000)
    {
      i_amt = (int)(100 * amt);
      printf("\n amt = %4.2f i_amt = %i", amt, i_amt);
      printf("\nYou entered $%i.%.2i -- Correct (Y/n)?", i_amt/100, i_amt%100);
      scanf("%c%*c", &yn);
      if (yn == '\0' OR yn == 'Y' OR yn == 'y')
        success = TRUE;
    }
  }
  return (i_amt);
}

And here's an output sample with the debug print statement:

Enter deposit amount (dollars and cents): $ 1118.58                             

 amt = 1118.58 i_amt = 111857                                                   
You entered $1118.57 -- Correct (Y/n)?                                          

Why is this happening, and how do I fix it?

After discussion in the comments below, I've converted the function to use integers, so there's no question of float precision (I hadn't really thought about just how little precision a standard float has on a 64-bit OS and compiler). Here's the updated version, which can't give this failure because there are no floats:

int get_amt (void)
{
  int scanned, amt1, amt2;
  int success = FALSE;
  char yn = '\0';

  while (NOT success)
  {
    scanned = scanf("%i.%i%*c", &amt1, &amt2);

    /* validate and verify amount */
    if (scanned >= 1)
    {
      printf("\nYou entered $%i.%.2i -- Correct (Y/n)?", amt1, amt2);
      scanf("%c%*c", &yn);
      if (yn == '\0' OR yn == 'Y' OR yn == 'y')
    success = TRUE;
    }
  }
  return (100*amt1 + amt2);
}
7
  • 2
    Hint: precision: your float value is actually like 1118.57999999999 Commented Jan 4, 2015 at 2:33
  • possible duplicate of Float to Int conversion Commented Jan 4, 2015 at 2:35
  • This is entered directly with two digits after the decimal; it's not a calculated value I'm converting. Where is the other .0000000001 going? If that's the problem, the obvious solution is to convert this function to input in integer, like scanf ("%i.%i%*c", amt1, amt2) -- but why is it a problem? Commented Jan 4, 2015 at 2:37
  • 1
    It doesn't matter that you entered the number directly. Internally, when 1118.58 is converted to binary, it can lose precision unless 0.58 were able to be described exactly as a finite sum of negative powers of 2 that fit within the IEEE float number of bits available. The binary system, with a fixed number of binary digits, can't exactly represent all possible, finite decimal expansions. Commented Jan 4, 2015 at 2:38
  • 1
    (int)100*1118.58f is also not an int, it's a float, which is why the check in the if passes. Commented Jan 4, 2015 at 2:43

1 Answer 1

5

As @MitchWheat has already alluded to in a comment above, it is likely that when you print out the float, it rounds up, whereas when you calculate the int, it simply truncates. Get around this by using the round function. I.e., i_amt = round(100 * amt);, which will round the number in the same manner as the float is rounded.

Sign up to request clarification or add additional context in comments.

16 Comments

You might also consider using a double for better precision or obtaining the value by a different means if you're planning on having more than 15 significant figures, which seems plausible if you're dealing with money.
@ZeissIkon: How exactly would this problem be in the past? Whatever number you put in, the computer still has to convert it to binary, and 0.58 simply cannot be converted exactly in binary, the same way 1/3 cannot be represented exactly in decimal.
@Dtor "more than 15 significant figures, which seems plausible if you're dealing with money." -- not in any personal checkbook I've ever seen. ;) Clearly, I need to change this to input in integer (fortunately, I can fix this once, since I put all the amount entries for the entire program through this one function).
@Zeiss it's not the Pentium math problem, it's how floats are defined. Floats only allow around 6 significant digits of precision, which in your case you lose the 6th due to how you converted it (not using round). Double only allows for 15 significant digits. The Pentium problem was something completely different, it was a mismatch in what it said it did versus what it accidentally did. Floats and doubles are well defined.
@ZeissIkon Actually, re-thinking the 15 digits again, maybe double isn't good enough -- if you're prompting the user to put in the US budget deficit! Haha...
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.