0

In some part of my program, I have an array which contains float numbers (for example 9.8, 5.0, 4.45). What I have to do is take those numbers to another array and if they are float (I mean if they have digits after the decimal point that are not zero), then multiply them by 10 enough times to make it an int value (so from the first example, after that I should have 98, 5, 445). I know it doesn't sound clear but it's hard for me to describe my problem better.

My attempt was to do this but I have errors.

for(i=0;i<size;i++)
    {
       if(teb[i]%10!=0)
       {
           while(teb[i]%1!=0)
           {
               teb[i]=teb[i]*10;
           }
       }
    }

I have error: invalid operator to binary % (have float and int) and I am not sure if I can use char or similar variables.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Matthew
  • 83
  • 7
  • 2
    Please provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) – Devolus Feb 18 '21 at 15:50
  • 1
    You should show the compiler errors — presumably, you have `float teb[] = { ... };` or something similar. In which case, you're being told that you can't apply the `%` operator to floating-point values. This will get tricky because most decimal fractions cannot be exactly represented in binary floating-point numbers — see [Is floating-point arithmetic broken?](https://stackoverflow.com/q/588004/15168) Will you ever need more than 2 decimal places? It might be best to convert to a string (`%g` perhaps), remove the decimal point, and convert to integer — circumlocuitous though that seems. – Jonathan Leffler Feb 18 '21 at 15:51
  • I have error: invalid operator to binary % ( have float and int) and i am not sure if i can use char or similar variables – Matthew Feb 18 '21 at 15:53
  • 2
    Yup: you can only apply the modulo operator to two integer values (C11 [§6.5.5 Multiplicative operators](http://port70.net/~nsz/c/c11/n1570.html#6.5.5)) — hence the error. The information should be in the question, though, not in a comment. – Jonathan Leffler Feb 18 '21 at 15:55
  • 4.45 will probably be stored as the 32-bit float value 4.44999980926513671875, but following your procedure, the integer 444999980926513671875 is likely to to overflow the integer type unless you use a 64-bit integer type such as `long long int`. – Ian Abbott Feb 18 '21 at 16:17
  • 9.8 cannot be represented in the formats commonly used for `float` and `double`. When you try, the result will be 9.80000019073486328125 or 9.800000000000000710542735760100185871124267578125. To fix this problem, we have to back up to before you converting some original 9.8 to the floating-point value. So we need to know where you get these numbers from, how they are originally represented, what characteristics about them are none, and then it may be possible to make recommendations about producing some integer representation of them. – Eric Postpischil Feb 18 '21 at 16:19
  • 2
    What you're trying to do is basically not possible. – klutt Feb 18 '21 at 16:44
  • 1
    This problem is both (a) meaningless and (b) impossible. I suspect it was given to you by a poorly-educated instructor, who is ignorant about the realities of binary floating-point arithmetic. It's meaningless because there's no use for the array of integers you end up with -- they don't have any consistent relationship to the original numbers. And its impossible because the original numbers (as storied in that array of floats) *do not have a number of "digits past the decimal"* that successive multiplications by 10 can meaningfully strip off. – Steve Summit Feb 18 '21 at 17:33
  • @IanAbbott 444999980926513671875 isn't even likely to fit in a `long long`! – Steve Summit Feb 18 '21 at 18:08

3 Answers3

1

In my opinion, you'll not be able to manage this sanely without using string formatting and then converting from string back to floating-point. I use double rather than float. This is the code I came up with, including your three test values as the first three.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static inline int all_same_as(char *str, char c)
{
    while (*str != '\0')
    {
        if (*str++ != c)
            return 0;
    }
    return 1;
}

static inline void zap_trailing(char *str, char c)
{
    char *end = str + strlen(str);
    while (end > str)
    {
        if (*(end - 1) != c)
            return;
        *--end = '\0';
    }
}

static inline void zap_leading(char *str, char c)
{
    char *end = str;
    while (*end == c)
        end++;
    if (end > str)
        memmove(str, end, strlen(end) + 1);  /* Not memcpy()! */
}

int main(void)
{
    double d[] =
    {
        9.8,
        5.0,
        4.45,
        1.0,
        18.0,
        9.25,
        8.719,
        2.78128,
        2721.0/1001.0,
        3.14159,
        355.0/113.0,
        0.001234,   /* Smaller */
        1.2345E+13, /* Biggest */
        1.2345E-13, /* Smallest */
    };
    enum { NUM_D = sizeof(d) / sizeof(d[0]) };

    for (int i = 0; i < NUM_D; i++)
    {
        char buffer1[32];
        snprintf(buffer1, sizeof(buffer1), "%.6f", d[i]);
        char buffer2[32];
        strcpy(buffer2, buffer1);
        zap_leading(buffer2, ' ');              /* Not needed with %.6f; was needed with %10.6f */
        char *dot = strchr(buffer2, '.');
        if (all_same_as(dot + 1, '0'))
            *dot = '\0';
        else
        {
            zap_trailing(dot + 1, '0');
            size_t len = strlen(dot + 1);
            memmove(dot, dot + 1, len + 1);     /* Not memcpy()! */
        }
        double iv = strtod(buffer2, NULL);
        printf("%8g = %8.2g = %8.3g = %10s (converted: %10s - [%s] %g)\n",
               d[i], d[i], d[i], buffer1, buffer2, buffer2, iv);
    }
    return 0;
}

Output:

     9.8 =      9.8 =      9.8 =   9.800000 (converted:         98 - [98] 98)
       5 =        5 =        5 =   5.000000 (converted:          5 - [5] 5)
    4.45 =      4.5 =     4.45 =   4.450000 (converted:        445 - [445] 445)
       1 =        1 =        1 =   1.000000 (converted:          1 - [1] 1)
      18 =       18 =       18 =  18.000000 (converted:         18 - [18] 18)
    9.25 =      9.2 =     9.25 =   9.250000 (converted:        925 - [925] 925)
   8.719 =      8.7 =     8.72 =   8.719000 (converted:       8719 - [8719] 8719)
 2.78128 =      2.8 =     2.78 =   2.781280 (converted:     278128 - [278128] 278128)
 2.71828 =      2.7 =     2.72 =   2.718282 (converted:    2718282 - [2718282] 2.71828e+06)
 3.14159 =      3.1 =     3.14 =   3.141590 (converted:     314159 - [314159] 314159)
 3.14159 =      3.1 =     3.14 =   3.141593 (converted:    3141593 - [3141593] 3.14159e+06)
0.001234 =   0.0012 =  0.00123 =   0.001234 (converted:    0001234 - [0001234] 1234)
1.2345e+13 =  1.2e+13 = 1.23e+13 = 12345000000000.000000 (converted: 12345000000000 - [12345000000000] 1.2345e+13)
1.2345e-13 =  1.2e-13 = 1.23e-13 =   0.000000 (converted:          0 - [0] 0)

You can make choices about how many decimal digits to support, etc. I chose to use up to 6 after the decimal point (format %.6f used with snprintf()). I included very small (1.23E-13) and very large (1.23E+13) values; the behaviour for even bigger or smaller values is similar.

I initially used %10.6f in the snprintf() statement. When I did that, values were printed with leading blanks. The zap_leading() function removes those. The code has since been revised to use %.6f and there are no leading blanks to zap. I left the code to zap leading characters in place; it could be removed too.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

Most decimal fractions expressed as some finite number of digits in base 10 cannot be stored exactly in the binary floating point representations typically used in most implementations.

If you know the numbers are only supposed to be accurate to a fixed number of decimal places n, you can first multiply by 10n and round to the nearest integer. Then strip off up to n trailing zero digits.

For example, for up to 3 decimal places:

for(i=0;i<size;i++)
{
   long x = round(teb[i] * 1000.0);
   for (int j = 0; j < 3; j++)
   {
       if (x % 10 == 0)
       {
           x /= 10;
       }
       else
       {
           break;
       }
   }
   teb[i] = x;
}
Ian Abbott
  • 15,083
  • 19
  • 33
0

Normally I don't provide answers to homework problems, but since this is such a bad problem, I'm making an exception.

Before I proceed, let me say that the original problem is meaningless and impossible. Since it's impossible, there's no good way to solve it. So the program I'm going to present has a number of problems, some quite serious.

The core of the program does what the problem asks: As long as the fractional part of one of the numbers is not 0, it multiplies the number by 10. Computing "the fractional part of the number" is performed by an auxiliary function I've written, fractionalpart(). First we'll look at the program, then I'll explain its remaining problems.

#include <stdio.h>

float in[] = {9.8, 5.0, 4.45};
int out[3];

float fractionalpart(float f)
{
    return f - (int)f;
}

int main()
{
    int i;
    for(i = 0; i < 3; i++) {
        float tmp = in[i];
        while(fractionalpart(tmp) > 0.001) {
            tmp = tmp * 10;
        }

        out[i] = tmp;
    }

    printf("out:\n");
    for(i = 0; i < 3; i++) printf("%d\n", out[i]);
}

The problems this has relate to the fact that, on an ordinary computer using ordinary floating-point representations, there is no such number as 9.8. There's no such number as 4.45, either. What you thought was the float number 4.45, for example, is represented internally as something like 4.44999980926513671875. So a "proper" version of this program would multiply it by 10 a total of twenty times, converting it to 444999980926513671875. But that number won't even fit in a 64-bit integer!

So this program cheats: it doesn't loop until the fractional part is exactly 0; it instead loops until the fractional part is less than 0.001. Where did that number come from? The answer is: nowhere, I pulled it out of the air, it seemed like a good guess. You can experiment with bigger or smaller fudge factors if you like.

The bigger problem -- and you've probably thought of this already -- is that the program assumes that the "fractional part" actually does go down to something like 0.001 or 0.0001. But that's not necessarily true! As we just saw, the number 4.45 is "really" 4.44999980926513671875 inside, so after multiplying by 10 twice, it looks like the program is going to have an integer value of 444 (not 445), and the fractional part is going to be 0.999980926513671875, which is not less than 0.001, so the program is going to keep going!

Actually, it doesn't keep going, it does get the "right answer", at least on my machine (and I think I know why), but there are probably plenty of numbers (roughly half of them, actually) where there will be an intermediate result more like 0.999 than 0.001, and this program will do the wrong thing.

I hate posting code with serious problems like this, it's very tempting to try to write an "improved" version, but it would be much messier, and really, the original question is such a horrible one that it just isn't worth it. (It would be instructive to figure out and explain why this program does seems to work, at least most of the time -- why it doesn't get confused by the occasional 0.999 value -- but I don't have time for that just now, either.)

Steve Summit
  • 45,437
  • 7
  • 70
  • 103