Duration - Why do extra work when seconds is negative?

106 Views Asked by At

This is the method toNanos in class Duration

public long toNanos() {
    long tempSeconds = seconds;
    long tempNanos = nanos;
    // TODO: Here makes me confused
    if (tempSeconds < 0) {
        // change the seconds and nano value to
        // handle Long.MIN_VALUE case
        tempSeconds = tempSeconds + 1;
        tempNanos = tempNanos - NANOS_PER_SECOND;
    }
    long totalNanos = Math.multiplyExact(tempSeconds, NANOS_PER_SECOND);
    totalNanos = Math.addExact(totalNanos, tempNanos);
    return totalNanos;
}

I couldn't understand why need to do extra work when seconds is negative.

Positive numbers' max is 2^63-1 and negative numbers' min is 2^63, looks like it transfer -2^63s,-1ns to -2^63+1s,1000_000_000-1ns, but it has to particate in calculate eventually. In my view this is meaningless because Math.multiplyExact and Math.addExact will throw an Exception when numbers overflow, it doesn't change not matter the judgement existed or not

2

There are 2 best solutions below

0
Thomas Kläger On BEST ANSWER

The extra work is needed for the case Duration.ofNanos(Long.MIN_VALUE) (and some more cases which result in the same number of seconds).

Duration.ofNanos(Long.MIN_VALUE) is stored as seconds=-9223372037 and nanos=145224192.

The simple calculation seconds*1_000_000_000 would underflow the range of long values (and therefore the naive Math.multiplyExact(seconds, NANOS_PER_SECOND) would throw an exception). But Duration.ofNanos(Long.MIN_VALUE).toNanos() should not throw an exception, it should return Long.MIN_VALUE.

They could have written the condition as if (tempSeconds <= -9223372037) { ... } and it would give the same results (i.e. throw for the same values of seconds and nanos) but such a constant is harder to maintain.

0
Sree Kumar On

I think this is due to the way the seconds and nanos are stored internally, while trying to make the usage of the API intuitive. You may have noticed this in the Duration's JavaDocs:

The model is of a directed duration, meaning that the duration may be negative.

You may also have noticed that the class stores the sign only for the seconds field. So, for a negative duration, the user provides arithmetically logical values using Duration.ofSeconds( long, int ) and the API "adjusts" the excess seconds and nanos into the corresponding fields.

Examples for negative duration:

  • -1999747475ns => Duration.ofSeconds( -2, 252525 )
  • -2000252525ns => Duration.ofSeconds( -2, -252525 )
  • -252525ns => Duration.ofSeconds( 0, -252525 )
  • -1000252525ns => Duration.ofSeconds( 1, -2000252525 )

The above values are "converted" to the right quantities for the seconds and nanos fields as per its definition. That is, nanos should be between 0 and 999_999_999. At this stage, the field seconds may be negative and the field nanos will be positive or zero.

Eg, in the case of 2nd example (-2, -252525), the values are converted to: enter image description here

Now, imagining the computation of toNanos() on a number line, the API has to employ the following methods after the above mentioned adjustment:

  • "Forward" duration or positive duration is: ( Number of seconds * NANOS_PER_SECOND ) + nanos
  • "Backward" duration or negative duration is: ( ( Number of seconds + 1 ) * NANOS_PER_SECOND ) + nanos

Once again, why "+ 1" in the case of negative? Because nanos has been converted to its right positive, reduced value (and the excess added to seconds) and travelling on the number line will always be forwards.