1

I would like to convert an int64_t value [nanoseconds] into a float (or double) [seconds] value.

So I tried the following:

int64_t elapsed_nano = 7079206912L;
printf("%f\n", float(elapsed_nano));
float elapsed_sec = float(elapsed_nano) / float(1000000000);
printf("%f\n", elapsed_sec);

That seems to cut off some of the last bit .. probably due to some internal rounding. Here the output:

7079206912.000000
7.079207

I tried to multiply with float(0.000000001) instead, but that did not help.

I guess best would be, to just change the exponent of the float, though I did not find any documentation on how that can be done.

I am using gcc 4.8.5 (cannot update to a more recent gcc for different reasons)

Alex
  • 1,602
  • 20
  • 33
  • This seems to be, as you said, a cut of the last bits. Instead of using a float, you could try float_32, that may allow you to reach a [better precision](https://en.wikipedia.org/wiki/Single-precision_floating-point_format) – totok Jul 22 '20 at 10:15
  • In which header float_32 is defined ? Cannot find any info about that type. – Alex Jul 22 '20 at 10:28
  • Alex, what C compiler are you using? – chux - Reinstate Monica Jul 22 '20 at 10:29
  • 3
    @AbhayAravinda: Multiplying by 1e-9 increases the number of rounding errors: 1e9 is exactly representable, but 1e-9 is not. So `x / 1e9f` incurs a rounding error only in the division, but `x * 1e-9f` incurs one rounding error in converting `1e-9f` to `float` and another in the multiplication. – Eric Postpischil Jul 22 '20 at 10:34
  • @EricPostpischil Sorry. Got rid of it – Abhay Aravinda Jul 22 '20 at 10:36
  • 3
    Using `float` and wanting accuracy are incompatible choices. Using `double` would improve your chances of getting meaningful results. – Jonathan Leffler Jul 22 '20 at 13:47
  • @chux - Reinstate Monica -- gcc v4.8.5 .. I cannot use a more recent gcc, because it is at work, where I dont have privileges to update. – Alex Jul 22 '20 at 14:13
  • @Jonathan Leffler, actually I only want to change the exponent. .. afaik the precision problem only seems to occur because a multiplication/division is required. I wonder why there are no methods for direct exponent modifications. The storage location of the exponent seems to be fixed. I guess that could save some performance and keep the precision in some cases (and possibly I could continue using float :) ) – Alex Jul 22 '20 at 14:22
  • 1
    You cannot "only [...] change the exponent". At least not in base 10. Your hardware uses binary, and the exponent is based on binary as well. A such, you can "change the exponent" to multiply/divide with any power of 2, but you cannot "change the exponent" to achieve the same with a power of 10. There is a factor of 5 in the later that must lead to rounding errors. – cmaster - reinstate monica Jul 22 '20 at 14:33
  • ok, thanks for the details ! That explains why there is no easy way .. did not know that the exponent is based on binary – Alex Jul 22 '20 at 14:48

1 Answers1

2

Hmm looks like a format issue too.

To see 9 digits after the decimal point, use printf("%.9f\n", elapsed_sec);.

Consider printf("%.*g\n", DBL_DIG, elapsed_sec); for a more general approach.


Use double instead of float. Unexpected output due to the limited precision of float - errors start becoming visible in float after 6 significant digits.


For |values| up to 224, float commonly has sufficient precision to encode an integer exactly.

For |values| up to 253, double commonly has sufficient precision to encode an integer exactly.

printf("%f\n", (double) elapsed_nano);
double elapsed_sec = elapsed_nano / 1000000000.0;
printf("%f\n", elapsed_sec);

It looks like OP is using a C++ compiler for C code.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Same result for double (should have mentioned that I already tried that). Yes, I am using a C++ compiler, does that matter ? IF there is a C++ way of fixing the problem, it as well would be very welcome ! – Alex Jul 22 '20 at 10:31
  • 1
    Tested it on this compiler (https://www.onlinegdb.com/). This works. But you need to change the printf from "%f" to "%1.9f". – Abhay Aravinda Jul 22 '20 at 10:31
  • 1
    @AbhayAravinda If you want to see 9 digits after the decimal point, use `printf("%.9f\n", elapsed_sec);`, else consider `printf("%.*g\n", DBL_DIG, elapsed_sec);` for a more general approach. – chux - Reinstate Monica Jul 22 '20 at 10:33
  • Re “equivalent to about 6 to 9 decimal digits”: [No.](https://stackoverflow.com/a/61614323/298225) – Eric Postpischil Jul 22 '20 at 10:36
  • 1
    That answer is based on there is no decimal digit equivalency. Common parlance of decimal precision yet does apply. It is like a planet orbit: is it a circle, no its an ellipse, no it an ellipse with perturbations, no its not a solvable n-body problem, ... 6 to 9 decimal digits serves for an reasonable intro into precision of a `float`. As one advanced understanding, your _no_ assertion applies. – chux - Reinstate Monica Jul 22 '20 at 10:45
  • No, it is not an ellipse. An ellipse reaches an outer bound and returns. Floating-point precision does not have an upper bound: A floating-point object may represent a number exactly, with “infinite precision.” “6 to 9 decimal digits” is not a reasonable intro because it teaches false concepts. Bad teaching like this is one of the reasons misunderstanding of floating-point is rife. And there is just no need for it; nobody needs to be told this, and they do not benefit from it. If one does not want to explain in detail, it suffices to say errors start becoming visible in `float` after 6 digits. – Eric Postpischil Jul 22 '20 at 10:55
  • @chux - Reinstate Monica - Ok, thanks, with changing to double and using the advanced printf I finally get the division done in a good way. (Though I am still suprised if there is no more simple solution, like just setting the exponent instead of doing a division) – Alex Jul 22 '20 at 14:10
  • @EricPostpischil "errors start becoming visible in float after 6 digits.' _is_ a good way to express it. – chux - Reinstate Monica Jul 22 '20 at 21:54