Thomas Blanquet
Thomas Blanquet

Reputation: 507

Decompose decimal part of a double


I need to decompose a decimal part of a number in single digits, but I need to get the most obvious representation. Here is my code, to be clearer :

#include <stdio.h>

void main(){
    double value = 0.123;
    int decim_tab[6];
    int decimal;
    int i;

    for (i = 0; i < 6; ++i) {
        value *= 10;
        decimal = (int)value;
        decim_tab[i] = decimal;
        value -= decimal;
    }
    for (i = 0; i < 6; ++i)
        print("%d\n", decim_tab[i]);
}

The output I need is :

1
2
3
0
0
0

But I get :

1
2
2
9
9
9

EDIT

A solution I found is to add a small delta to the value in order to force the shortest representation :

#include <stdio.h>

void main(){
    double value = 0.123;
    int decim_tab[6];
    int decimal;
    int i;

    value += 0.000000001
    for (i = 0; i < 6; ++i) {
        value *= 10;
        decimal = (int)value;
        decim_tab[i] = decimal;
        value -= decimal;
    }
    for (i = 0; i < 6; ++i)
        print("%d\n", decim_tab[i]);
}

I would be happy to find a better way, any suggestions ?

Upvotes: 4

Views: 172

Answers (3)

Florian
Florian

Reputation: 99

The reason you get unexpected output is that decimal fractions cannot always be exactly represented using (most common) base two floating point numbers. Using printf("%.20f", value); after your assignment of value you will see that the value 0.123 is actually being stored as 0.12299..., which is why you receive that output.

If you only need to print out six digits, you can use string formatting of floating point numbers:

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

int main(){
    double value = 0.123;
    char *s = malloc(9);

    sprintf(s++, "%.6f", value);
    while(*s++){
        putchar(*s);
        putchar('\n');
    }
}

EDIT: The code in my answer is very specific to the example you gave, so when using it be aware that I made some assumptions, e.g. your value will never have more than one digit before the decimal point.

Upvotes: 3

Mecki
Mecki

Reputation: 133019

Floating point numbers are not exact value representation. Here's a simple sample:

double a = 0.15 + 0.15; // 0.15 + 0.15 == 0.3, right?
double b = 0.1 + 0.2;   // 0.1 + 0.2 == 0.3, right?
if (a == b) {
  printf("Equal\n");
} else {
  printf("Unequal\n");
}

What will that print? Equal? Are you sure? Try it yourself:

http://rextester.com/VZOZ1043

It prints Unequal, that's because there are some numbers that floating point can't represent exactly and that's something you always need to keep in mind when doing floating point math. Also there is rounding involved in many operations, so the results of math operations are as good as possible but not always "exact", there's a tiny error that can also sum up if you run multiple operations.

double value = 0.123;

// Assuming none of your numbers has more than 58 digits,
// one period and one termination char.
char buffer[60];

// Print the number to the buffer.
// Missing: Error checking if it did fit!
snprintf(buffer, sizeof(buffer), "%f", value);

// Find the period or end of string
int idx = 0;
for (; buffer[idx] && buffer[idx] != '.'; idx++);

// Print anything after the period till 
// the end of the string
if (buffer[idx] == '.') {
    for (idx++; buffer[idx]; idx++) {
        printf("%c\n", buffer[idx]);
    }
}

Test it: http://rextester.com/CYDQO24769

Upvotes: 1

chqrlie
chqrlie

Reputation: 144780

If you want 6 decimal places, you should add 0.0000005 (ie 0.5e-6) to round the value to the nearest place. This method will work for positive numbers, first extract the sign, then work on the absolute value.

Upvotes: 1

Related Questions