3

I would like to have a numeric variable that contains integer number with nanosecond precision. I tried this:

Instant t = Instant.now();
a = BigDecimal.valueof(t.getEpochSecond() * 1e9 + t.getNano());
b = BigDecimal.valueof(t.getEpochSecond() * 1e9 + t.getNano() + 1);

Both a and b are contains same value (e.g. 1.60681511777265408E+18), the +1 operation is lost here.

Any idea how to deal with this precision? The goal of it is to keep nanosecond-precision timestamp for time column in InfluxDB. I understand it has something to do with double precision (Java BigDecimal difference), but I haven't found any working solution yet.

Michal Špondr
  • 1,337
  • 2
  • 21
  • 44
  • One problem is that 1e9 is a `double`. That means that the argument expression for `valueOf` is computed in floating point .... and you are losing precision. – Stephen C Dec 01 '20 at 09:52
  • 1
    It is also worth noting that the nanosecond clock probably doesn't have nanosecond precision. In fact, the output shown in the answers implies that microsecond precision is what you are likely to get. – Stephen C Dec 01 '20 at 09:54
  • Why use a numeric type at all? `Instant` already has nanosecond precision. Doesn’t it give you everything you want? – Ole V.V. Dec 02 '20 at 20:34
  • @OleV.V. `Instant` holds nanoseconds of each second but can not give you a single value which I need to timestamp value in InfluxDB. – Michal Špondr Dec 04 '20 at 12:22
  • Thanks for answering. Makes sense. If it were me, if InfluxDB has got a `timestamp with time zone` type, I’d use it, otherwise convert the `Instant` to string and store as `char` or `varchar` for better readability. – Ole V.V. Dec 04 '20 at 13:26

2 Answers2

3

If you're storing an integer value, why use a BigDecimal instead of a BigInteger?

import java.time.*;
import java.math.*;

public class MyClass {
    public static void main(String args[]) {
      Instant t = Instant.now();
      BigInteger a = BigInteger.valueOf(t.getEpochSecond() * 1_000_000_000
              + t.getNano());
      BigInteger b = BigInteger.valueOf(t.getEpochSecond() * 1_000_000_000
              + t.getNano() + 1);

      System.out.println(a); // 1606816120696314000
      System.out.println(b); // 1606816120696314001
    }
}

Need to switch to BigInteger math methods in the year 2262 though, because the long argument will start overflowing.

Robby Cornelissen
  • 91,784
  • 22
  • 134
  • 156
2

Just do the operations with the methods in BigDecimal. And make sure you don't use valueOf(double). You can use valueOf(long), however.

BigDecimal a = BigDecimal.valueOf(t.getEpochSecond())
            .multiply(BigDecimal.valueOf(1_000_000_000))
            .add(BigDecimal.valueOf(t.getNano()));

BigDecimal b = BigDecimal.valueOf(t.getEpochSecond())
            .multiply(BigDecimal.valueOf(1_000_000_000))
            .add(BigDecimal.valueOf(t.getNano()))
            .add(BigDecimal.ONE);

Printing a and b gives something like:

1606815981554921000
1606815981554921001
Sweeper
  • 213,210
  • 22
  • 193
  • 313