0

As a beginner, I am working on a C program.

#include <stdio.h>
int main() {
    int speed, limit_num;
    printf("please input :\n");
    scanf("%d %d", &speed, &limit_num);

    if ((float)speed >= limit_num * 1.5)
    {
        printf("Exceed %.0f%%. License Revoked\n", (float)(speed - limit_num)/limit_num * 100);
    }
    else if ((float)speed >= (limit_num * 1.1))  
    {
        printf("Exceed %.0f%%. Ticket 200\n", (float)(speed - limit_num)/limit_num * 100);
    }
    else
    {
        printf("OK!\n");
    }
    return 0;
}

if I input"110 100", the output is"OK".

But theoretically, I think it should be "Exceed 10%. Ticket 200" , because (float)speed means a float value, and the result of limit*1.1 means a float value.

I tried to add the following code

    printf("%f, %f\n", (float)limit_num * 1.1, (limit_num *1.1));

And input "110 100", here is the output

110.000000, 110.000000

From this point of view, it does not solve my puzzle.

Hope can get help, thank you.

yeon hu
  • 3
  • 1
  • 1
    The cause is the rounding of operations on floating numbers. We must therefore avoid the equality test on floating numbers. – CGi03 Sep 01 '23 at 10:06
  • 1
    `(float)100` is `100.000000000000000000000000`, but `100*1.1` is `110.0000000000000142108547152020037174224853515625`, because the `double` number `1.1` is slightly bigger than the actual `1.1`. – mch Sep 01 '23 at 10:25

2 Answers2

1

Typically, the method to run away from floats is to multiply both sides by a big enough number so that fractions are not needed. In this case, just multiply both sides by 10.

if (speed * 10 >= limit_num * 15)
else if (speed * 10 >= limit_num * 11)  
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • If speed is `(UINT_MAX -10)`, and limit_num is `(UINT_MAX - 20)`, what will happen? – Tom Sep 01 '23 at 10:47
  • @Tom Those expressions would overflow. But where in the world do you drive with `UINT_MAX` km/h or mph or whatever. (Btw `speed` and `limit_num` are `int`, not `unsigned int` and @KamilCuk used `* 10` not `+ 10`. ) – Joel Sep 01 '23 at 10:53
  • @Joel If only to solve to problem in the world, it can declare speed and limit_num as `float` or `double`, also speed have no unit(such as km/h or m/s or other) – Tom Sep 01 '23 at 10:58
  • @Tom bro what are you on about?? OP has problems with floating point imprecision, this answer solves it, and you want to use floating point types again? And speed has the SI unit of m/s? – Joel Sep 01 '23 at 11:03
  • @Joel  Sorry, My English is poor, first, my mean is if speed and limit_num is less a little than max value that they can hold, then speed * 10, and limit_num * 15, what will happen( consider at c programmming)? second, OP's problem is not clear state speed unit(such as km/h or s/m or other), third, if only to solve to problem in the real world, then can declare speed and limit_num as float or double for `solve real exist problem`. – Tom Sep 01 '23 at 11:10
  • `what will happen` Overflow, in case of signed overflow, that's undefined behavior - it is not defined what will happen. The solution for that is prevent that overflow, modernly by using stdckdint.h. `OP's problem is not clear` OP problem is that calculation using floating point types introduce a rounding error that manifests in imprecise comparisons. To be precise, use integers. – KamilCuk Sep 01 '23 at 11:19
1

As elucidated by @mch in the comments above and explained in the linked article, floating point can only approximate most fractional values (owing to base10-base2 differences and the non-infinite number of bits.)

Vehicular speed is typically expressed in whole numbers.

The OP code suggests two thresholds (110 & 150) at which drivers would be penalised.

Another way to approach this problem is to recognise that speeds up to 109 are "Okay!", and speeds up to 149 carry a fine. It is at, or exceeding, the thresholds that is relevant. Sadly, "at" (equality) can be tricky when dealing with floating point values.

Below is an alternative approach toward solving this conundrum:

#include <stdio.h>

int main( void ) {
    int speed, limit_num;

    printf( "please input :\n" );
    if( scanf( "%d %d", &speed, &limit_num ) != 2 ) { // ALWAYS verify input succeeded
        fprintf( stderr, "Bad input\n" );
        exit(1);
    }

    float excess = (float)(speed - limit_num) / limit_num * 100;
    if( excess < 10 ) // less than 10% excess is allowed
        puts( "OK!" );
    else {
        printf( "Exceed %.0f%%. ", excess )
        // 10% to 49.9999% => fine imposed. 50% or more means cycling to work.
        if( excess < 50 )
            puts( "Ticket 200" );
        else
            puts( "License Revoked" );
    }

    return 0;
}

Just go with what works...

Fe2O3
  • 6,077
  • 2
  • 4
  • 20
  • Why do you close vote as duplicate and post an answer at the same time? – Lundin Sep 01 '23 at 11:23
  • @Lundin Boredom... Seriously, it's one thing to _firehose_ a beginner with long explanations about "inverse powers of two". Sometimes they just want to see any way around the problem that they are facing... Thus, this answer's example code. – Fe2O3 Sep 01 '23 at 11:27
  • 1
    This helps me a lot...I think I need to learn more about float point. Thank u! – yeon hu Sep 01 '23 at 11:57
  • I left the use of `float` in this answer. In this century, use `double` instead (if there are no imposed memory constraints.) Greater precision, and, I've read, the compiler will promote `floats` to `double` in most cases anyway... Cheers! – Fe2O3 Sep 01 '23 at 12:07