Rounding Doubles without round() Question

Well, I have spent ~3 hours trying to figure out what should be a simple solution. I'd greatly appreciate some help.

I'm trying to solve a kattis problem:
https://open.kattis.com/problems/romans

Basically I get some user input (double), do some math, and print the result as a rounded int.

I first tried:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <math.h>

int main() {
    // 1.
    double englishMiles = 0.0;
    std::cin >> englishMiles;
    
    // 2.
    double romanMiles = 0.0;
    romanMiles = (englishMiles * 1000 * 5280)/4854;
    
    // 3.
    std::cout << std::round(romanMiles) << '\n';
    return 0;
}

But for some reason this does not work on Kattis.

I then tried the following instead:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <string>
#include <cstring> // for auto testing
#include <cassert> // for auto testing
#include <cmath> // std::ceil(), std::floor()

void kattis();
void test();
int roundNumStr(const std::string &romanMiles);


int main(int argc, char* argv[]) {
    if (argc > 1 && strncmp(argv[1], "test", 4) == 0) {
        test();
    }
    else {
        kattis();
    }

    return 0;
}


void kattis() {
    std::string englishMiles;
    std::cin >> englishMiles;
    std::cout << "Debug: englishMiles str = " << englishMiles << '\n';
    
    std::string romanMiles;
    romanMiles = std::to_string( std::stod(englishMiles) * 1000 * 5280/4854.0 );
    std::cout << "Debug: std::stod(englishMiles) = " << std::stod(englishMiles) << '\n';
    std::cout << "Debug: romanMiles str = " << romanMiles << '\n';

    int romanMilesNum = roundNumStr(romanMiles);

    std::cout << romanMilesNum << '\n';
}

// for each char in romanMiles str, check if char == '.'
// - if it does, check if the num after the decimal >= 5.
//      * If it is >= 5 round up. else round down. Then return value as an int
int roundNumStr(const std::string &romanMiles) {
    const int romanStrSize = romanMiles.size();
    for (int i = 0; i < romanStrSize; ++i) {
        if (romanMiles[i] == '.') {
            std::cout << "Debug: i+1 = " << romanMiles[i+1] << '\n';
            if (romanMiles[i+1] >= 5) {
                return std::ceil( std::stod(romanMiles) );
            }
            else {
                return std::floor( std::stod(romanMiles) );
            }
        }
    }
    return -1; // If there is no '.' in string it will return -1.
}


void test() {
    std::string test1 = "3.3", test2 = "3.5", test3 = "3.9", test4 = "4";

    assert(roundNumStr(test1) == 3);
    //assert(roundNumStr(test2) == 4);
    //assert(roundNumStr(test3) == 4);
    //assert(roundNumStr(test4) == -1);

    std::cout << "All test cases passed!\n";
}

3.2 // input
Debug: englishMiles str = 3.2
Debug: std::stod(englishMiles) = 3.2
Debug: romanMiles str = 3480.840544
Debug: romanMiles[i+1] = 8
3481 // output

This seems to work, but when I do ./a.out test in bash to use the test case(s) then it says:
Debug: romanMiles[i+1] = 3
a.out: Romans.cpp:100: void test(): Assertion `roundNumStr(test1) == 3' failed.
Aborted (core dumped)
1
2
3
4
5
6
7
#include <iostream>
int main()
{
    double englishMiles;
    std::cin >> englishMiles;
    std::cout << (int)( englishMiles * 1000 * 5280 / 4854 + 0.5 ) << '\n';
}
Last edited on
lastchance wrote:
1
2
3
4
5
6
7
#include <iostream>
int main()
{
    double englishMiles;
    std::cin >> englishMiles;
    std::cout << (int)( englishMiles * 1000 * 5280 / 4854 + 0.5 ) << '\n';
}

Thank you for your reply! Could you please explain what you're doing? I tried looking it up but haven't found it yet. is (int) (...) basically just: int(...) which converts ... to int type?

I tried:
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
int main()
{
    double roundTo3 = 3.33;
    std::cout << (int)( roundTo3 + 0.5 ) << '\n';
    
    double roundTo4 = 3.501;
    std::cout << (int)( roundTo4 + 0.5 ) << '\n';

    return 0;
}

3
4

I don't understand why this works. roundTo3 (3.33) + .5 would be 3.83 which I would expect to be rounded up to 4 like roundTo4 does.
Last edited on
(int) just effects a "cast", i.e. a change of type, in this case to integer.

When changing type to integer you truncate toward zero, not round. Thus, thinking about the decimal places involved you would be doing the same as rounding if you added 0.5 first.
lastchance wrote:
(int) just effects a "cast", i.e. a change of type, in this case to integer.

When changing type to integer you truncate toward zero, not round. Thus, thinking about the decimal places involved you would be doing the same as rounding if you added 0.5 first.

Ahh okay. I see. So anything < num.5 will equal num when converted. While anything >= 5 will turn into num+1.# and when converted to int it is num+1.

Examples (Note to self):
3.3 + 0.5 = 3.8 (double converted to int = 3)
3.5 + 0.5 = 4.0 (double converted to int = 4)
3.7 + 0.5 = 4.2 (double converted to int = 4)

Thank you!
Note adding +0.5 and then casting is not exactly the same behavior as std::round.
There's at least one edge case where std::round is correct when the add-0.5-and-cast method is not.
https://stackoverflow.com/a/47302585/8690169
https://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1

Apparently it's only one pathological case though... in practical terms this is not an issue, and doing +0.5 is fine. But for something that requires the utmost accuracy, it is something to watch out for.
Last edited on
Ganado wrote:

Note adding +0.5 and then casting is not exactly the same behavior as std::round.
There's at least one edge case where std::round is correct when the add-0.5-and-cast method is not.
https://stackoverflow.com/a/47302585/8690169
https://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1

Apparently it's only one pathological case though... in practical terms this is not an issue, and doing +0.5 is fine. But for something that requires the utmost accuracy, it is something to watch out for.

Ah okay, thank you for letting me know. :)
Topic archived. No new replies allowed.