0

I would like to use the first five digits of a number for computation. For example, A floating point number: 4.23654897E-05

I wish to use 4.2365E-05.I tried the following

#include <math.h>
#include <stdio.h>

float num = 4.23654897E-05;
int main(){
float rounded_down = floorf(num * 10000) / 10000;
printf("%f",rounded_down);
return 0;

}

The output is 0.000000.The desired output is 4.2365E-05. In short,say 52 bits are allocated for storing the mantissa.Is there a way to reduce the number of bits being allocated?

Any suggestions on how this can be done?

Natasha
  • 1,111
  • 5
  • 28
  • 66
  • 1
    [check this first](https://stackoverflow.com/q/588004/2173917). – Sourav Ghosh Jan 30 '18 at 07:36
  • 10
    How does the title have anything to do with the question? There's no memory allocation being asked about? The title should be a short one-line summary of the actual thing you ask about. Otherwise it's pretty useless. – Some programmer dude Jan 30 '18 at 07:36
  • Take a look at [Rounding Number to 2 Decimal Places in C](https://stackoverflow.com/questions/1343890/rounding-number-to-2-decimal-places-in-c) and the like. It's easy enough to Google for these things. In fact, I just noticed that it linked on the right hand side of this page and would have been suggested to you when you posted your question. get into the habit of checking those suggestions before posting. – Mawg says reinstate Monica Jan 30 '18 at 07:37
  • I think you should try %E instead of %f. – No Em Jan 30 '18 at 07:38
  • OP probably wants to round the number to 5 valid digits. – Marian Jan 30 '18 at 07:42
  • 1
    note that `4.23654897E-05` is not a float and can't be represented closely in single-precision float (which has only ~7 digits of precision) – phuclv Jan 30 '18 at 07:43
  • Print `4.23654897E-05*10000`, then explain why you think `floor` should round it the way you want. – n. m. could be an AI Jan 30 '18 at 08:03
  • Or just print `4.23654897E-05` in the first place... – Lundin Jan 30 '18 at 08:05
  • Why do you want to use only five digits? Discarding imformation is usually not useful. Are you rying to fix rounding errors? The correct answer depends on what your ultimate purpose is. Better results are usually produced by keeping all available bits until the end. – Eric Postpischil Jan 30 '18 at 12:00
  • Part of your question requests reducing decimal digits. Part asks about reducing bits. Which do you really want? Veltkamp-Dekker splitting reduces bits. That is different from rounding to decimal digits. – Eric Postpischil Jan 30 '18 at 12:02
  • 1
    @EricPostpischil I'm interested in using only five digits in my computation in order to check how the loss in digits manifests as error, while performing arithmetic operations. – Natasha Jan 30 '18 at 12:50
  • @Natasha If `num = 5.123499E-05`, would you want the result to be about `5.1234E-05f` or `5.1235E-05`? – chux - Reinstate Monica Jan 30 '18 at 16:37

4 Answers4

2

A number x that is positive and within the normal range can be rounded down approximately to five significant digits with:

double l = pow(10, floor(log10(x)) - 4);
double y = l * floor(x / l);

This is useful only for tinkering with floating-point arithmetic as a learning tool. The exact mathematical result is generally not exactly representable, because binary floating-point cannot represent most decimal values exactly. Additionally, rounding errors can occur in the pow, /, and * operations that may cause the result to differ slightly from the true mathematical result of rounding x to five significant digits. Also, poor implementations of log10 or pow can cause the result to differ from the true mathematical result.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
1

The result is expected. The multiplication by 10000 yield 0.423.. the nearest integer to it is 0. So the result is 0. Rounding can be done using format specifier %f to print the result upto certain decimal places after decimal point.

If you check the return value of floorf you will see it returns If no errors occur, the largest integer value not greater than arg, that is ⌊arg⌋, is returned. where arg is the passed argument.

Without using floatf you can use %e or (%E)format specifier to print it accordingly.

printf("%.4E",num);

which outputs:

4.2365E-05

After David's comment:

Your way of doing things is right but the number you multiplied is wrong. The thing is 4.2365E-05 is 0.00004235.... Now if you multiply it with 10000 then it will 0.42365... Now you said I want the expression to represent in that form. floorf returns float in this case. Store it in a variable and you will be good to go. The rounded value will be in that variable. But you will see that the rounded down value will be 0. That is what you got.

float rounded_down = floorf(num * 10000) / 10000;

This will hold the correct value rounded down to 4 digits after . (not in exponent notation with E or e). Don't confuse the value with the format specifier used to represent it.

What you need to do in order to get the result you want is move the decimal places to the right. To do that multiply with larger number. (1e7 or 1e8 or as you want it to).

user2736738
  • 30,591
  • 5
  • 42
  • 56
  • 1
    This answer explains why the result is correct given the output. Correct me if I am wrong. – user2736738 Jan 30 '18 at 07:43
  • 1
    Hmmm. OP says: "I would like to use the first five digits of a number for computation." It sounds to me like the goal is to round the value to five significant places for use in a computation, not to simply print five places. OP could be more clear on this point, though. – ad absurdum Jan 30 '18 at 08:05
  • @DavidBowling.: Then how come OP is bothered about the representation or printing it? The question is inherently ambiguous. Now if I change the answer there will be a thought going on that I have been influenced by other answers. This is undesirable. – user2736738 Jan 30 '18 at 08:08
  • 1
    I agree that the question could be better worded, but I don't think that it is ambiguous. OP clearly states: "I would like to use the first five digits of a number for _computation_." Further: "I wish to _use_ 4.2365E-05." It doesn't seem that OP is worried about the printed representation except in that it is not as expected when the value of `rounded_down` is checked. – ad absurdum Jan 30 '18 at 08:13
  • @DavidBowling.: Edited. – user2736738 Jan 30 '18 at 08:20
  • @DavidBowling.: Do you have any feedback regarding the DV? Or any feedback overall? – user2736738 Jan 30 '18 at 10:08
  • Feedback: from the edit history I see that at first the OP question was not answered, but instead you said that the result was to be expected, given the OP code. This is obvious, and not an answer. In its current form, the answer seems incorrect: "...`= floorf(num * 10000) / 10000;` This will hold the correct value rounded down to 4 digits after `.`." is wrong, or at the least unclear. Instead of multiplying by `10000`, OP needs to multiply by `10000 * 10e5` to get the desired 4 decimal places. Also note that the final value has been truncated, not rounded. – ad absurdum Jan 30 '18 at 15:41
  • "I guess you get the idea what you need to do..." suggests that maybe _you_ understand how to fix OP code, but given the previous mis-statement, the overall answer seems unclear. Any of these may have been a reason for a downvote. – ad absurdum Jan 30 '18 at 15:41
  • Minor: Note that `4.2365E-005` is not the expected output per C. `4.2365E-05` is expected (2 digits exponent). Perhaps you are using non-compliant or early C compiler? – chux - Reinstate Monica Jan 30 '18 at 16:04
  • @DavidBowling.: That's way of saying ..I am not guessing answer I am saying that it should be clear to OP now. Secondly regarding the first point..`4.23E-05*10000..` I said that 4 dig after decimal point if it is written on expanded form not with exponent as you might have thought. Anyway I edited... – user2736738 Jan 30 '18 at 16:16
  • @DavidBowling.: Thanks. – user2736738 Jan 30 '18 at 16:25
  • @chux.: Thanks btw. My earlier comment said that I ran it in old compiler...now I ran it in latest gcc compiler - it gave the result as expected. – user2736738 Jan 30 '18 at 16:30
1

I'd go:

printf("%.6f", num);

Or you can try using snprintf() from stdlib.h:

float num = 4.23654897E-05;  char output[50];

snprintf(output, 50, "%f", num);

printf("%s", output);
Gaurav Pathak
  • 1,065
  • 11
  • 28
Krzysztof Bielak
  • 463
  • 2
  • 4
  • 11
1

I would like to use the first five digits of a number for computation.

In general, floating point numbers are encoded using binary and OP wants to use 5 significant decimal digits. This is problematic as numbers like 4.23654897E-05 and 4.2365E-05 are not exactly representable as a float/double. The best we can do is get close.

The floor*() approach has problems with 1) negative numbers (should have used trunc()) and 2) values near x.99995 that during rounding may change the number of digits. I strongly recommend against it here as such solutions employing it fail many corner cases.

The *10000 * power10, round, /(10000 * power10) approach suffers from 1) power10 calculation (1e5 in this case) 2) rounding errors in the multiple, 3) overflow potential. The needed power10 may not be exact. * errors show up with cases when the product is close to xxxxx.5. Often this intermediate calculation is done using wider double math and so the corner cases are rare. Bad rounding using (some_int_type) which has limited range and is a truncation instead of the better round() or rint().

An approach that gets close to OP's goal: print to 5 significant digits using %e and convert back. Not highly efficient, yet handles all cases well.

int main(void) {
  float num = 4.23654897E-05f;

  //         sign   d   .   dddd   e   sign   expo + \0
  #define N (1    + 1 + 1 + 4    + 1 + 1    + 4    + 1)
  char buf[N*2];  // Use a generous buffer - I like 2x what I think is needed.

  // OP wants 5 significant digits so print 4 digits after the decimal point.
  sprintf(buf, "%.4e", num);

  float rounded = (float) atof(buf);
  printf("%.5e %s\n", rounded, buf);
}

Output

4.23650e-05 4.2365e-05

Why 5 in %.5e: Typical float will print up to 6 significant decimal digits as expected (research FLT_DIG), so 5 digits after the decimal point are printed. The exact value of rounded in this case was about 4.236500171...e-05 as 4.2365e-05 is not exactly representable as a float.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Could you please explain the following lines of the code '#define N (1 + 1 + 1 + 4 + 1 + 1 + 4 + 1) char buf[N*2]; ' – Natasha Jan 31 '18 at 02:02
  • The `1 + 1 + 1 + 4 + 1 + 1 + 4 + 1` relates to the comment above it - a buffer of up to 14 characters may be needed to print a floating point value with 5 significant digits. Having said that, little reason to be overconfident about potential buffer needs - so I double it (2x). – chux - Reinstate Monica Jan 31 '18 at 02:07
  • Sorry I didn't understand this _a buffer of up to 14 characters may be needed to print a floating point value with 5 significant digits_. Also,please explain the difference between _float num = 4.23654897E-05f; and float num = 4.23654897E-05_ – Natasha Jan 31 '18 at 06:56
  • @Natasha Are you familiar with the difference between `float` and `double`? Apply that difference here: `4.23654897E-05f` is a `float` constant. `4.23654897E-05` is a `double` constant. – chux - Reinstate Monica Jan 31 '18 at 13:52
  • Would it be wrong if we define as **float num = 4.23654897E-05** and **double num=4.23654897E-05** – Natasha Jan 31 '18 at 14:00
  • @Natasha Nothing wrong with `double num=4.23654897E-05;` if changing the type of `num` is OK with you. – chux - Reinstate Monica Jan 31 '18 at 15:24
  • I'm confused.My understanding is, using float num = 4.23654897E-05 will allow a compiler to assign four bytes, double num = 4.23654897E-05 will assign 8 bytes.When you say _4.23654897E-05f is a float constant. 4.23654897E-05 is a double constant_ do you mean when we use float num = 4.23654897E-05 8 bytes are allocated and for float num = 4.23654897E-05f 4 bytes are allocated? – Natasha Jan 31 '18 at 16:30
  • @Natasha With `float num = 4.23654897E-05;`, compilers convert text "4.23654897E-05" to an 8-byte `double`. As that `double` is being assigned to a 4-byte `float`, that `double` is converted to a `float` and assigned to `num`. With `float num = 4.23654897E-05f;`, compilers convert text "4.23654897E-05f" to an 4-byte `float` and assign that to `num`. Both will certainly emit the same code. The 1st may also raise a warning like "warning: conversion to 'float' alters 'double' constant value" as `4.23654897E-05` and `4.23654897E-05f` have nearly the same, but different values. The 2nd will not. – chux - Reinstate Monica Jan 31 '18 at 16:43
  • @Natasha To simplify: `0.1 != 0.1f` as 0.1 cannot be represented exactly as a `double` nor `float`. Given `float/double` have different preciseness, the approximated values used differ. – chux - Reinstate Monica Jan 31 '18 at 16:47
  • float numf = 4.23654897E-05f; float num = 4.23654897E-05; printing these values upto 15 decimal places on a gcc compiler gives 4.236548920744099e-05 4.236548920744099e-05 as output.I didn't get any warning too – Natasha Feb 01 '18 at 02:36
  • @Natasha A difference in output is not expected as both `float`s have the same value. `4.23654897E-05f` and `4.23654897E-05` are different values. Compilation did not get a warning because not all your warnings are enabled: -Wfloat-conversion in gcc.. ( or weak compiler). Note that what you call "15 decimal places" is 16 significant decimal digits. – chux - Reinstate Monica Feb 01 '18 at 03:00