haskell / time

A time library
http://hackage.haskell.org/package/time
Other
119 stars 79 forks source link

nominalDiffTimeToSeconds - strange result #194

Closed bapcyk closed 2 years ago

bapcyk commented 2 years ago

I am trying to do:

t0 <- getCurrentTime
-- delay 5 seconds
t1 <- getCurrentTime
let d = diffUTCTime t1 t0
-- round d == 5  (strange, but OK)
let delta = nominalDiffTimeToSeconds d
-- round delta == 5  (super weird)

I have a function for a conversion from any Fixed to another one. And I supposed that if I have picoseconds (5 in the case), it means 5 x 10⁻¹² seconds: nominalDiffTimeToSeconds really returns the value as picoseconds: nominalDiffTimeToSeconds :: NominalDiffTime -> Pico. This is how I (and I believe many others persons) do understand the function returning picoseconds according to its signature.

Sure, my conversion function returns 0, because 5 seconds ≠ 5 picoseconds, obviously.

After some digging I found:

So, my questions are:

mySuperConvertor :: forall from to a b. (Fixed a ~ from, Fixed b ~ to, HasResolution a, HasResolution b) => from -> to
mySuperConvertor a = let ar = resolution a; br = resolution (undefined :: Fixed b) in fromRational $ toRational a * (br % ar)

I have feeling that it's some weird behavior/design of time's functions, but maybe I'm missing something. Maybe documentation of the module is not clean enough, please, show what is the logically right and consistent method to work with the Pico result from the nominalDiffTimeToSeconds function and how to understand the case. Thanks! :)

AshleyYakeley commented 2 years ago

Pico does not mean picoseconds. It is a fixed-point type with a resolution of 10^-12.

nominalDiffTimeToSeconds converts a NominalDiffTime to seconds (not picoseconds), just as its name says, which is why it gives 5 for five seconds.

bapcyk commented 2 years ago

Pico does not mean picoseconds. It is a fixed-point type with a resolution of 10^-12. nominalDiffTimeToSeconds converts a NominalDiffTime to seconds (not picoseconds), just as its name says, which is why it gives 5 for five seconds.

OK, I just checked < 1 seconds intervals and I really get 0 seconds :)

But to be honest I still don't understand the idea of such a signature. Let's say we have a clock, maybe atomic clock, IDK, some atomic timer. And I use it to measure time interval between 2 events: t1 and t0. And I see on its frontal panel an indicator - "5 PICO". I read it as "the delta t is 5 picoseconds". If it shows me "5 SEC", it means 5 seconds. I see in the signature of the nominalDiffTimeToSeconds exactly this "PICO".

I never see on the frontal panel of the timer something like "5 FEMTO" because my timer has a femtasecond resolution (its resolution is a technical/implementation detail and I don't expect to see it in the semisegments indicators), otherwise how to understand what it measured?

I mean, then why does nominalDiffTimeToSeconds returns Pico and not Uni? You said "the resolution", but I tried to measure delta time of 50 microseconds and sure the function returns me 0 seconds, OK. It means that it's resolution is literally 1 second, no any picos in the picture.

Anyway, from my point of view, such signature looks very deceptive.

AshleyYakeley commented 2 years ago

On the frontal panel you should see the indicator nominalDiffTimeToSeconds. That's exactly what it does.

Pico does not mean picoseconds. It means pico resolution.

The actual resolution depends on your clock. This is what I get:

Prelude Data.Time> t0 <- getCurrentTime
Prelude Data.Time> t1 <- getCurrentTime
Prelude Data.Time> let d = diffUTCTime t1 t0
Prelude Data.Time> nominalDiffTimeToSeconds d
7.231037469000

Notice the 000 at the end? That's because the Pico type has pico resolution, but my clock apparently has nanosecond resolution.

bapcyk commented 2 years ago

That's because the Pico type has pico resolution, but my clock apparently has nanosecond resolution.

OK, then:

  1. why should we have the information about the resolution of some abstract clock in the returning type of the function?
  2. if to follow this logic, then all time related functions must return Pico - because we have some abstract clock with Pico resolution
  3. If I want to know the resolution of my system clock, I suppose function like getSysClockResolution :: Integer or similar and I don't want to have this Pico in a function returning time delta treated as a seconds-based intervals (Haddock: Conversion functions such as fromInteger and realToFrac will treat it as seconds.)
  4. it's resolution of what? Not of the real system clock, the function treats it as a 1 second resolution when I try to treat it as an integer.

Well, what is the problem to have Uni as a return type?

Sorry, I completely cannot catch the idea. Maybe the idea is to know the denominator of the ratio? I use Fixed as time units, is it wrong? If yes, then why?

AshleyYakeley commented 2 years ago

NominalDiffTime has a resolution of picoseconds. So nominalDiffTimeToSeconds converts it to a type that has a resolution of 10^-12.

Pico is defined as type Pico = Fixed E12. It's a fixed-precision type, that wraps around an Integer.

If you want the actual resolution of your clock, it's getTime_resolution :: DiffTime.

bapcyk commented 2 years ago

Then I cannot understand the next:

let x = 5 :: Pico

what does it mean? I was sure it's something * 10⁻¹². And if it's about time intervals, then it's 5 picoseconds. If it's about grams, then it's 5 picogram. But you tell me that 5 :: Pico can be 5 seconds, 5 milliseconds, 5 picoseconds and everything else, but - with a resolution of 1 picosecond. It's a first strange logic to me.

The second one is why would somebody want to know the resolution of the time interval if it losts it being converted to integral - round returns me just 5 without picoseconds accurancy ("resolution")?

I think about Pico in the signature as about measure unit. But you told me that it's not the unit but the resolution! It's very weird. Even more, the resolution of what - getTime_resolution is 1 nanosecond.

Maybe I cannot get the logic. I used to think that if I have a function like personAge :: Person -> Year then it returns the age in years, and I would not interesting about any resolutions of the timer measured the age of the person.

As I understand "the resolution" literally means the denominator of the ratio. When I got 5 as a result and the denominator is 10¹² then it's the ratio:

            5
------------------
1000000000000

and how it can be 5 seconds? If the nominalDiffTimeToSeconds returns seconds (as its name says it) but with picosecond's resolution, OK, then it has to return something which leads to 5000000000000 after the round (with possible noise). Where is the mistake?

AshleyYakeley commented 2 years ago

let x = 5 :: Pico means x is five.

Read it like this:

let
    x :: Pico
    x = 5

The line x=5 mean x is five. Not 5*10^-12. Not five picoseconds. Not even five seconds. Just the number five. Nothing else. Just, exactly, the plain ordinary number five. That's all it is. That's why it says x=5, as in, x is equal to five.

Now the type of x is Pico, which is the same as Fixed E12. That's a fixed-point type that happens to have resolution of 10^-12. So for example, if you do this:

let
    y :: Pico
    y = 0.000000000003
    z :: Pico
    z = 0.0000000000003

Then y will be greater than zero, but z will be equal to zero, because that number is smaller than Pico resolution.

bapcyk commented 2 years ago

hm... It's interesting. OK, then the last question, if I use Pico, Milli and similar as units (for time and so on) - could it be a problem (completely irrelevant to nominalDiffTimeToSeconds) - directly or like:

newtype Sec = Sec Uni deriving newtype (...)

?

They always have values > 1, so I cannot lost the valuable digits like in your example (eg, 0.0005::Milli) and I prefered them instead of a special libs for units/time-units and still had not any problems, is it OK?

AshleyYakeley commented 2 years ago

Uni is essentially just the same as Integer. If you used it to represent seconds, it can only be a whole number of seconds.