Rounding Doubles without round() Question

Sep 14, 2019 at 12:45pm
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)
Sep 14, 2019 at 7:46pm
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 Sep 14, 2019 at 7:59pm
Sep 15, 2019 at 11:42pm
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 Sep 15, 2019 at 11:43pm
Sep 16, 2019 at 4:30am
(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.
Sep 20, 2019 at 5:53pm
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!
Sep 20, 2019 at 5:58pm
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 Sep 20, 2019 at 6:00pm
Sep 27, 2019 at 10:27pm
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.