2

When i try to extract the fractional part of a double it seems to be rounding down in C, Is there a way to do it without rounding?

    double t = 8.2;
    int ipart = (int)t;
    long long val = abs((long long)(t*1000000));
    long long fpart = (val)%1000000;

fpart gives 199999, Is there a way to get it as 200000 without rounding down? Tried many ways but none of the methods seems to be working for all the numbers.

Intention is to finally convert this double into string which should have the exact value as "8.20000". If i can extract fraction part in long long variable then i can generate the string using snprintf.

Scarlet
  • 69
  • 6

2 Answers2

1

How to extract fractional part of a double value without rounding ...?

Use modf() from the standard C library.

#include <math.h>

double ipart;
double fraction = modf(value, &ipart);
printf("Fraction %g\n", fraction);
printf("Whole number %g\n", ipart);

The modf functions break the argument value into integral and fractional parts, each of which has the same type and sign as the argument. They store the integral part (in floating-point format) in the object pointed to by iptr.
C17dr § 7.12.6.12 2


Deeper into 8.2

double can represent about 264 different values exactly. 8.2 is not one of them.

With double t = 8.2;, t takes on a nearby value, which is exactly 8.199999999999999289457264239899814128875732421875 or
81801439850948192/9007199254740992 due to the binary nature of common double.
To find the fraction, use fraction*pow(2,DBL_MANT_DIG)/pow(2,DBL_MANT_DIG).

Thus the goal of "to get it as 200000 without rounding down" as 200000/denominator for the fraction part of t is not possible.

chux - Reinstate Monica
  • 113,725
  • 11
  • 107
  • 213
  • 1
    I think you probably wanted `double fraction = modf(value, &ipart);` – njuffa May 11 '21 at 02:52
  • @chux: Thanks for the clear explanation but my intention is to convert this double into string , i was not able to get exact value using snprintf, thats why thought of extracting the fraction in long and then convert to string. But this way looks like i have to get it in double again which again will give the same rounded value. – Scarlet May 11 '21 at 05:10
  • @chux: Also when i do "long long val = abs((long long)(t*10000)); ", val becomes 82000 but "long long val = abs((long long)(t*1000000)); " val becomes 8199999, Any idea why there is a difference? – Scarlet May 11 '21 at 06:50
  • 1
    @Scarlet `8.199999999999999289457264239899814128875732421875 * 10000` rounds closest to `82000.0` or just above. `8.199999999999999289457264239899814128875732421875 * 1000000` rounds closest to just under `8200000.0`. Conversion to `long long` in the 2nd case lops off a 0.999... (note: `abs()` is an `int` function applied to a `long long`. Research `llabs(), lround()`) – chux - Reinstate Monica May 11 '21 at 08:02
0

The value 8.2 can't be exactly represented in binary floating point. The actual value is closer to 8.19999999999999929.

Because of this, you're forced to round:

long long val = llabs(round(t*1000000));

Or add 0.5 to the value before converting:

long long val = llabs((t*1000000) + 0.5);
dbush
  • 162,826
  • 18
  • 167
  • 209
  • there's some problems with the `0.5` approach, e.g. gives the wrong result for negative numbers , and very large numbers. `round` is designed to cope with all cases – M.M May 11 '21 at 02:28