4
\$\begingroup\$

I have a class with a data member that needs to be rounded up to a 2 digit integer, irrespective of the number of the input digits.

For example:

roundUpto2digit(12356463) == 12 
roundUpto2digit(12547984) == 13 // The 5 rounds the 12 up to 13.

Currently my code looks like:

int roundUpto2digit(int cents){
    // convert cents to string
    string truncatedValue = to_string(cents);
    // take first two elements corresponding to the Most Sign. Bits
    // convert char to int, by -'0', multiply the first by 10 and sum the second 
    int totalsum =  int(truncatedValue[0]-'0')*10 + int(truncatedValue[1]-'0');
    // if the third element greater the five, increment the sum by one
    if (truncatedValue[2]>=5) totalsum++; 
    return totalsum;
} 

How can this be made less ugly?

\$\endgroup\$
  • \$\begingroup\$ You've got a bug in your code. You forgot the singlequotes on if (truncatedValue[2]>='5') totalsum++; \$\endgroup\$ – Snowbody Mar 31 '15 at 13:23
  • 2
    \$\begingroup\$ What if you have less than 2 digits initially? E.g. roundUpto2digit(1)? \$\endgroup\$ – Aleksey Demakov Mar 31 '15 at 13:32
  • \$\begingroup\$ @ Aleksey Demakov that is a valid question. A line checking for length and if statement including only the MSB will do it. Thanks! \$\endgroup\$ – Ziezi Mar 31 '15 at 13:38
  • 1
    \$\begingroup\$ What should be the result of roundUpto2digit(999)? Following your code the result will be 100. But this is a 3-digit integer. Is this okay or should it be further truncated to 10 to make a 2-digit integer? \$\endgroup\$ – Aleksey Demakov Mar 31 '15 at 14:35
  • \$\begingroup\$ @Aleksey Demakov one more error. Yes, it should always be 2-digit integer. \$\endgroup\$ – Ziezi Mar 31 '15 at 14:53
6
\$\begingroup\$

If I understood your requirements correctly then it might be like this:

int roundUpto2digit(int cents) {
  if (cents < 100)
    return cents < 10 ? cents * 10 : cents;
  while ((cents + 5) >= 1000)
    cents /= 10;
  return (cents + 5) / 10;
}

The test:

#include <stdio.h>

void
test(int i) {
    printf("%d -> %d\n", i, roundUpto2digit(i));
}

int
main() {
    test(0);
    test(1);
    test(5);
    test(9);
    test(10);
    test(49);
    test(50);
    test(94);
    test(95);
    test(99);
    test(100);
    test(104);
    test(105);
    test(994);
    test(995);
    test(999);
    test(1000);
    test(1040);
    test(1050);
    return 0;
}

The result:

0 -> 0
1 -> 10
5 -> 50
9 -> 90
10 -> 10
49 -> 49
50 -> 50
94 -> 94
95 -> 95
99 -> 99
100 -> 10
104 -> 10
105 -> 11
994 -> 99
995 -> 10
999 -> 10
1000 -> 10
1040 -> 10
1050 -> 11

It is uncertain if [1, 9] range should map into [10, 90] (2 digits) or [1, 9] (1 digit). I could fix it, if the later case is true.

\$\endgroup\$
4
\$\begingroup\$

With some math you can avoid the string conversion.

First take the log10 of the input:

double lg10 = log10(input);

The integral part of that will contain the exponent while the fractional part will contain the digits of the mantissa.

Putting just the fractional part in the exponent of 10^exponent will create a number between 1 and 9.999 which is scaled by an exact power of ten. So we can multiply that number by 10 and round it for the result:

double frac = lg10 - floor(lg10); //note round to -inf to get the integral part
return (int)round(10*pow(10, frac));

This can overflow from 99.9 to 100 so we need to catch that.

Putting it all together results in:

int roundUpto2digit(int cents){
    if(cents == 0)
        return 0;

    double lg10 = log10(cents);
    double frac = lg10 - floor(lg10); 

    int result = (int)round(10*pow(10, frac));
    return result==100 ? 10 : result;
}
\$\endgroup\$
1
\$\begingroup\$

I don't know about making the code more elegant, but a faster way is to use a chain of ifs:

int mostSignificantThreeDigits(int value){
    if (value < 1000) return cents;
    if (value < 10000) return cents/10;
    if (value < 100000) return cents/100;
    // etc.
}

int nearestMultipleOf10Factor(int value){
    return (value+5)/10;
}

int truncatedToTwoDigits(int value){
    return nearestMultipleOf10Factor(mostSignificantThreeDigits(value));
}

It's also possible to use a loop for mostSignificantThreeDigits():

int mostSignificantThreeDigits(int value){
    while (value > 1000) {
      value /= 10;
    }
    return value;
}

but I suspect it's slower due to the repeated divisions.

int mostSignificantThreeDigits(int value){
    int factor = 1000;
    int divisor = 1;
    while (value > factor) {
      factor *= 10;
      divisor *= 10;
    }
    return value / divisor;
}

might be faster if integer multiplication is way faster than integer division.

\$\endgroup\$
  • \$\begingroup\$ The original code is undefined for values in the [0, 99] range. There will be out of bounds sting access in this case. Also there is ambiguity for values [995, 999]. The result will be 100, a 3-digit value, which contradicts the task description to get just 2 digits. With your version the result is the same in this case. For the former case your version will return a single-digit value. For instance, the return value for argument 50 will be 5. This is again contradicts the requirement to return always 2 digits. \$\endgroup\$ – Aleksey Demakov Mar 31 '15 at 15:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.