-2

I am reading "float" numbers which are up to 2 decimal places, such as below:

122
122.3
122.34

and need to convert them to integer value by multiplying (imagine storing dollars/cents)

int i;
double d;

scanf( "%lf", &d );

i = d * 100;

for example18.56 will be transformed into 1855

Is there any way to read value as a double and convert it to int correctly?

TIA

notnull
  • 1,908
  • 3
  • 19
  • 25
  • 4
    Please give an example that will cause loss of precision and how you think it should be handled. – Gabe Dec 04 '13 at 23:13
  • 2
    The 'precision loss' is already there in the floating-point variable. Multiplying it by 100 won't cause any damage. – user207421 Dec 04 '13 at 23:14
  • Never ever use floating point if you really care about the digits. Look at http://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency – Guido Dec 04 '13 at 23:14
  • Unfortunately, can't use long long. – notnull Dec 04 '13 at 23:15
  • Float has 23 bits of precision, int 32 (usually these days). – Alan Stokes Dec 04 '13 at 23:16
  • 2
    Take the input as a string, then convert the string to an int (after getting rid of the decimal). – nhgrif Dec 04 '13 at 23:17
  • Well, precision loss is not exact description of my problem, but you got the point. I just wanted to make sure, there is no way to handle it, while reading as double... Thanks – notnull Dec 04 '13 at 23:21
  • So what *is* the 'exact description of your problem'? No way to handle what? – user207421 Dec 04 '13 at 23:22
  • Question updated. Sounds better now? – notnull Dec 04 '13 at 23:43
  • @user2997251 dealing with inexactness of `double` is easy if your input, once mathematically multiplied by `X` (e.g. 100) results in a whole number. Performing `round(d*X)` takes care of that. If your numbers, in text form, are exactly half way like "12.345", you need to specified your rounding criteria. – chux - Reinstate Monica Dec 05 '13 at 03:52
  • @Alan Stokes I would suggest instead that `float` has [24 bits](http://en.wikipedia.org/wiki/Binary32#IEEE_754_single-precision_binary_floating-point_format:_binary32) of precision and a sign bit. `int` typically has 31 bit of precision and a sign bit. – chux - Reinstate Monica Dec 05 '13 at 03:54

5 Answers5

5

The simplest way to convert to an int with rounding:

i = (int)floor(d * 100 + 0.5);

If your numbers are always positive:

i = (int)(d * 100 + 0.5);

Values between 1.565 and 1.575 will become 157. Values between 2.135 and 2.145 will become 214.

That way, if you have a number like 1.56999, it will become 157.

This does not "solve precision loss" - whatever that meant. It's just rounding.

If a value is exactly between two numbers (e.g. d is 1.125) then you might get unexpected results. In this case, all numbers have only two decimal places, so this is not a problem. If numbers were not limited to more decimal places, you'd want to consider more carefully how to handle numbers like 1.125.

user253751
  • 57,427
  • 7
  • 48
  • 90
1

The problem is that your conversion to float (via scanf) is an approximation which is losing the intended exact decimal value. Instead use:

int dollars = 0, cents = 0;
scanf("%d.%d", &dollars, &cents);
cents += 100*dollars;

or similar.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • With that said, I agree that my code is not robust. For example, it accepts some nonsense like `1.-2`, and misinterprets inputs with only one digit after the decimal point. The point is to suggest the right approach. – R.. GitHub STOP HELPING ICE Dec 05 '13 at 00:05
  • Imo, this is not the right approach. If you read from `stdin`, second `%d` will take next integer from the input, even if it wasn't right after decimal point. Sure, you can check return value, but you can't read it anymore. – notnull Dec 05 '13 at 01:52
0

The value stored in you floating point will be rounded. Multiplying by a power of 10 will just magnify the error. If you want don't want have any rounding error, you'll need to read the value immediately into an integer, possibly tracking the location of the decimal point if you need the factor by which the value was multiplied:

std::string tmp;
if (std::cin >> tmp) {
    std::string::size_type decimal_point = tmp.rfind('.');
    int power(0);
    if (decimal_point != tmp.npos) {
        tmp.erase(tmp.begin() + decimal_point);
        power = int(decimal_point) - int(tmp.size());
    }
    int value;
    if (std::istringstream(tmp) >> value) {
        std::cout << "read " << value << "e" << power << "\n";
    }
}

This code will only deal with relatively simple floating point numbers but certainly with ones of the form you quoted.

Another alternative is to use decimal floating points which don't have the rounding problem when processing decimal values. Sadly, they are not, yet, available as part of the standard C++ library. Some compiler do provide an extension to deal with decimal floating points, however.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
0

If you are sure the numerals have at most two digits and are of moderate magnitude' reading them as double, multiplying by 100, and rounding to the nearest integer will produce the correct result. (Simply casting a double to an integer truncates, not rounds to nearest, and will sometimes produce incorrect results due to floating-point rounding errors. You can use the round function to round.)

Using double to process inputs in this way is not necessarily the best approach.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

OP almost had it, just need to add round() and error checking.

round() nicely rounds to nearest, ties toward 0.

Values like 12.34 will result in 1234.

Values at the 1/2 cent boundary like 12.345 may return 1234 or 1235 due to FP issues, but OP 3 examples did not venture beyond 0.01.

No problem with + or - numbers.

#include <math.h>
...
int i;
double d_dollars;
double d_cents;

if (1 != scanf( "%lf", &d_dollars)) handle_error();
d_cents = round(d_dollars * 100.0);
if ((d_cents < INT_MIN) || (d_cents > INT_MAX)) handle_error();
i = (int) d_cents;
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256