Converting milliseconds to seconds

I

Ilya Zakharevich

I was stupid enough to write the following subroutine:

sub truncate_ms_to_sec ($) {int(0.001 * shift)}

It was supposed to be used with integer values. All was fine. A
couple of days ago I got a bug report.

My current conjecture is that it is an unfortunate property of 0.001
with 64-bit IEEE numbers. You see: these are results of rounding
0.001 to the machine precision with different length of mantissa:

52 => 0.0010000000000000000208166817117216851329
53 => 0.0010000000000000000208166817117216851329 <== 64-bit IEEE
54 => 0.0010000000000000000208166817117216851329
....
63 => 0.0010000000000000000000643745039913268258
64 => 0.00099999999999999999995849538558453928338 <== 80-bit IEEE
65 => 0.0010000000000000000000114349447879330546

As a result, if you perl is configured to use 64-bit floats, the
result of 0.001 * $n is going to be a tiny bit above the "exact
result". int() would shave the error (since it truncates down).

But with 80-bit IEEE, the result of 0.001 * $n is a tiny bit SMALLER
than the "exact result"; hence if exact value of 0.001 * $n happens to
be an integer, the exact value with machine-precision-0.001 is
slightly smaller than an integer; by IEEE semantic, this exact value
should be rounded to a closest machine-precision value to get the
machine-precision value.

I expect that for some values of $n (obviously, divisible by 1000),
the result of this rounding is "one tick below" an integer; then int()
would shave 1 of this value, so the subroutine returns 1-smaller
value.

In short: the subroutine is buggy, but one would not discover this
with 64-bit IEEE numbers.

Hope this would help somebody, ;-)
Ilya

P.S. in GP/PARI: f(n) = n=n+9; floor(0.001*2^n+0.5)*1./2^n
 
P

Paul Lalli

I was stupid enough to write the following subroutine:

  sub truncate_ms_to_sec ($) {int(0.001 * shift)}

It was supposed to be used with integer values.  All was fine.  A
couple of days ago I got a bug report.

perldoc -f int
int EXPR
int Returns the integer portion of EXPR. If EXPR is
omitted, uses $_. You should not use this function
for rounding: one because it truncates towards 0,
and two because machine representations of floating
point numbers can sometimes produce counterintuitive
results.


perldoc -q round
Does Perl have a round() function? What about ceil() and
floor()? Trig functions?

Remember that int() merely truncates toward 0. For rounding
to a certain number of digits, sprintf() or printf() is
usually the easiest route.


Paul Lalli
 
I

Ilya Zakharevich

[A complimentary Cc of this posting was sent to
Paul Lalli
perldoc -f int
int EXPR
int Returns the integer portion of EXPR. If EXPR is
omitted, uses $_. You should not use this function
for rounding: one because it truncates towards 0,
and two because machine representations of floating
point numbers can sometimes produce counterintuitive
results.

This is just a reflection of a sorry state of Perl documentation.
There is NO OTHER WAY to round-to-0 but to use int(). Yes, this may
produce counterintuitive results; NO, there is no better way.

Hope this helps,
Ilya
 
J

Jürgen Exner

Ilya Zakharevich said:
[A complimentary Cc of this posting was sent to
Paul Lalli
perldoc -f int
int EXPR
int Returns the integer portion of EXPR. If EXPR is
omitted, uses $_. You should not use this function
for rounding: one because it truncates towards 0,
and two because machine representations of floating
point numbers can sometimes produce counterintuitive
results.

This is just a reflection of a sorry state of Perl documentation.
There is NO OTHER WAY to round-to-0 but to use int().

Your statement is non sequitur. int() does not an never was supposed to
round a number. If you expect int() to round a number then you are mistaken
about the semantic of this function.
Yes, this may
produce counterintuitive results; NO, there is no better way.

Well, there _are_ ways how to round a number in Perl, and because int()
doesn't round in the first place all of them are "better" than int().
Hope this helps,

Hardly

jue
 
I

Ilya Zakharevich

[A complimentary Cc of this posting was sent to
Jürgen Exner
Your statement is non sequitur. int() does not an never was supposed to
round a number. If you expect int() to round a number then you are mistaken
about the semantic of this function.

I assure you that int() does nothing else but rounding-to-0.

Hope this helps,
Ilya
 
J

John W. Krahn

Ilya said:
[A complimentary Cc of this posting was sent to
Jürgen Exner
Your statement is non sequitur. int() does not an never was supposed to
round a number. If you expect int() to round a number then you are mistaken
about the semantic of this function.

I assure you that int() does nothing else but rounding-to-0.

Then why does the documentation say:

perldoc -f int
int EXPR
int Returns the integer portion of EXPR. If EXPR is omitted,
uses $_. You should not use this function for rounding: one
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
because it truncates towards 0, and two because machine
^^^^^^^^^
representations of floating point numbers can sometimes
produce counterintuitive results. For example,
"int(-6.725/0.025)" produces -268 rather than the correct
-269; that’s because it’s really more like
-268.99999999999994315658 instead. Usually, the "sprintf",
"printf", or the "POSIX::floor" and "POSIX::ceil" functions
will serve you better than will int().


John
 
J

Jürgen Exner

Ilya Zakharevich said:
[A complimentary Cc of this posting was sent to
Jürgen Exner

Please don't do that. I happen to read the NGs in which I am posting.
I assure you that int() does nothing else but rounding-to-0.

As I said: you are greatly mistaken about the intended semantic of int().
You may want to read the documentation for clarification.

jue
 
I

Ilya Zakharevich

[A complimentary Cc of this posting was NOT [per weedlist] sent to
Jürgen Exner
I happen to read the NGs in which I am posting.

If you think this is enough, you know little about Usenet propagation
semantic. (By definition: unreliable.)
As I said: you are greatly mistaken about the intended semantic of int().
You may want to read the documentation for clarification.

As I said, the existing state of documentation of Perl is, at most,
pitiful. (If you still do not realize this, I wrote some
[significant?] part of implementation of Perl's int(). *This* is why
I feel so hurt by this bug of mine. ;-)

Hope this helps,
Ilya
 
M

magoo

Ilya,

Thank you for sharing your hard-won information.

It is much appreciated by myself and certainly by others who may not
explicitly say so.

It is still amazing to me to hear of round-off errors and is a
reminder to all to be on guard
when computing numeric results. Experts as well as novices can be
bitten by these evil bugs when
they least expect it.

Thanks again for sharing your knowledge with the rest of us.

T. Michaels
 
J

Joost Diepenmaat

magoo said:
Ilya,

Thank you for sharing your hard-won information.

It is much appreciated by myself and certainly by others who may not
explicitly say so.

It is still amazing to me to hear of round-off errors and is a
reminder to all to be on guard
when computing numeric results. Experts as well as novices can be
bitten by these evil bugs when
they least expect it.

While I suspect your post is sarcastic, I do think it's wise to point
out that many people - some even experienced programmers - have a
simplistic (which is to say *wrong*) view of what happens when floating
point numbers are represented in binary (which is what's used by
default in most if not all commerically available computers today).

The basic point is: there are many fractional numbers that can be
notated without errors in decimal (like 0.1) which *cannot* be represented
correctly in the finite binary floating point notation used by most
CPUs, in the same way that 1/3 cannot be notated in a finite decimal
floating point (i.e. 1.3333333..., which by the way can't be correctly
represented in binary either).

This means that some floating point numbers that we tend to think of as
"easy and finite" because of our familiarity with the decimal notation
are not in fact finite to the processor that happens to run the
calculation. Which leads to errors in the result where you may not
expect to find any.

Most languages that go for performance see this as an acceptable
trade-off, at least by default, and perl is one of those languages.

Anyway, all this means that blindly truncating floats (i.e, using int())
to "round down" *will* result in errors. In fact, all rounding
will. We just have a more acute sense of the error when 0.999999999
gets rounded down to 0. To avoid that, use (s)printf to use a more
appropriate rounding rule.

Also note that on my machine at least, sprintf uses "banker's rounding",
which IMHO is a good choice, but can also confuse matters even further.

See also:
http://en.wikipedia.org/wiki/Floating_point
http://en.wikipedia.org/wiki/Banker's_rounding

Hope this helps someone at least,
Joost.
 
J

Joost Diepenmaat

CPUs, in the same way that 1/3 cannot be notated in a finite decimal
floating point (i.e. 1.3333333..., which by the way can't be correctly
represented in binary either).

I meant 0.33333333....

Man, these numbers are confusing!

Joost
 
M

magoo

Joost,

My post was in no way intended to be sarcastic.

It was my intention only to thank Ilya (for whom I have nothing
but admiration and respect)
for sharing with us the information he did.

How many of us could have written about our own mistakes in a
public forum, with the good
intention of helping others? Not many I suspect, I for one do not
like to publicize my mistakes.

Your post was informative as well, and added more information to
the discussion which is a good
thing. The earlier discussion seemed to be getting bogged down and
headed nowhere.

As you say, numbers and especially their computer representation,
can get confusing!

Best regards,

T. Michaels
 
J

Joost Diepenmaat

magoo said:
Joost,

My post was in no way intended to be sarcastic.

It was my intention only to thank Ilya (for whom I have nothing
but admiration and respect)
for sharing with us the information he did.

I think he was taking an obviously wrong-headed view of what it was that
was causing the problem, though maybe it could have been explained a bit
clearer to him in this thread.

Anyway, as I said, confusion with regards to floats are common and I don't
blame him or anyone else who makes that mistake.

I also think that perl's documentation could at least include a few (more?)
pointers to explain the issues with floating point representation.

Where I do have a problem is when Ilya claims, quite vehemently, that
this is some sort of bug in perl, while it is purely due to the
inherent inaccuracies in the processor and/or C library that happens to
implement floating point semantics, which you will *have* to learn to
deal with when programming with floats in most any language. In other
words: unless the language you're using is explicitly documented to use
decimal notation for floating points on the platform you're using you
should assume it cannot represent all finite decimal floats, and could
be off in any direction by some amount. Because that is the case for most
languages and processors in use today.

Joost.
 
I

Ilya Zakharevich

[A complimentary Cc of this posting was NOT [per weedlist] sent to
Ilya Zakharevich
As I said, the existing state of documentation of Perl is, at most,
pitiful. (If you still do not realize this, I wrote some
[significant?] part of implementation of Perl's int(). *This* is why
I feel so hurt by this bug of mine. ;-)

Thinking about it more, this illusion of grandeur may be a piece of my
imagination ;-). I remember a lot of struggle to make int() and "%"
conformant to `perldoc perlnumber'; I definitely implemented at least
one of these - but I do not remember which.

Sorry,
Ilya
 
J

Jürgen Exner

Jürgen Exner said:
As I said: you are greatly mistaken about the intended semantic of int().
You may want to read the documentation for clarification.

Ilya, you are right.
It would never have occured to me that the term "to round" could be used to
mean truncation of trailing digits without balancing. But according to
friends and Wikipedia this is quite common.
Please accept the appologies of a non-native English speaker.

jue
 
I

Ilya Zakharevich

[A complimentary Cc of this posting was NOT [per weedlist] sent to
Jürgen Exner
It would never have occured to me that the term "to round" could be used to
mean truncation of trailing digits without balancing. But according to
friends and Wikipedia this is quite common.

There are 9 (vairably useful) semantics of finding an integer nearby:

going in direction of -infty (=down)
going in direction of +infty (=up)
going in direction to 0
going in direction away from 0;
going to closest (if ambiguous, one of 4 variants listed above);
going to closest (if ambiguous, to even).

It is very convenient to be able to use one verb to describe them all;
"to round" comes quite handy.

[(The last way is the IEEE way (used in any "serious context"),
"to-closest-or-away-from-0" is the "naive way" (one which they teach
in schools - and not used anywhere else).]

Hope this helps,
Ilya
 
P

Peter J. Holzer

Ilya Zakharevich said:
[A complimentary Cc of this posting was sent to
Jürgen Exner

Please don't do that. I happen to read the NGs in which I am posting.

[ perldoc -f int: You should not use this function for rounding]
As I said: you are greatly mistaken about the intended semantic of int().
You may want to read the documentation for clarification.

Saying that to Ilya is a bit humorous.

Ilya is completely correct that int does round to 0 (repeat: round to
zero - not round to nearest!). And that is the intended semantics -
perldoc -f int says so: "Returns the integer portion of EXPR".

But I think Ilya is a barking up the wrong tree here:

Of course there is no other way to round-to-0, but the manual is talking
about "rounding" here: And "rounding" without any qualifiers usually
means "round to nearest", not "round to 0".

The entry is still confusing: The hint to use sprintf instead is ok, but
in what way are floor and ceil supposed to be better than int? And the
caveat about decimal/binary confusions isn't really a reason for "don't
use int for rounding" - any rounding method has the same problem.

hp
 
N

nolo contendere

Ilya Zakharevich said:
[A complimentary Cc of this posting was sent to
Jürgen Exner
Please don't do that. I happen to read the NGs in which I am posting.

[ perldoc -f int: You should not use this function for rounding]
As I said: you are greatly mistaken about the intended semantic of int()..
You may want to read the documentation for clarification.

Saying that to Ilya is a bit humorous.

Ilya is completely correct that int does round to 0 (repeat: round to
zero - not round to nearest!). And that is the intended semantics -
perldoc -f int says so: "Returns the integer portion of EXPR".

But I think Ilya is a barking up the wrong tree here:

Of course there is no other way to round-to-0, but the manual is talking
about "rounding" here: And "rounding" without any qualifiers usually
means "round to nearest", not "round to 0".

The entry is still confusing: The hint to use sprintf instead is ok, but
in what way are floor and ceil supposed to be better than int? And the
caveat about decimal/binary confusions isn't really a reason for "don't
use int for rounding" - any rounding method has the same problem.

Ok, so this is a stupid question. What's wrong with the following?

sub round {
my $number = shift;
return int($number + .5);
}
 
N

nolo contendere

[A complimentary Cc of this posting was sent to
Jürgen Exner
Please don't do that. I happen to read the NGs in which I am posting.
<[email protected]>], who wrote in article <[email protected]>:
[ perldoc -f int: You should not use this function for rounding]
This is just a reflection of a sorry state of Perl documentation.
There is NO OTHER WAY to round-to-0 but to use int().  
Your statement is non sequitur. int() does not an never was supposedto
round a number. If you expect int() to round a number then you are mistaken
about the semantic of this function.
I assure you that int() does nothing else but rounding-to-0.
As I said: you are greatly mistaken about the intended semantic of int().
You may want to read the documentation for clarification.
Saying that to Ilya is a bit humorous.
Ilya is completely correct that int does round to 0 (repeat: round to
zero - not round to nearest!). And that is the intended semantics -
perldoc -f int says so: "Returns the integer portion of EXPR".
But I think Ilya is a barking up the wrong tree here:
Of course there is no other way to round-to-0, but the manual is talking
about "rounding" here: And "rounding" without any qualifiers usually
means "round to nearest", not "round to 0".
The entry is still confusing: The hint to use sprintf instead is ok, but
in what way are floor and ceil supposed to be better than int? And the
caveat about decimal/binary confusions isn't really a reason for "don't
use int for rounding" - any rounding method has the same problem.

Ok, so this is a stupid question. What's wrong with the following?

sub round {
    my $number = shift;
    return int($number + .5);

}

Well, for positive numbers, I mean.
 
J

Joost Diepenmaat

nolo contendere said:
Well, for positive numbers, I mean.

It does a naive round to nearest: it rounds x + 0.5 to x+1 for *all
integers* x. In other words, it's biased: round(0) + round(0.5) +
round(1) + round(1.5) == 4. while you would really rather want it to be
2. That sort of bias can really affect your statistical or financial
calculations.

Generally this is "solved" by rounding to the nearest even number in
the (int+0.5) case. Which is what sprintf does on my system. Note that
this is only really a working strategy when the numbers you're rounding
have a reasonable range (i.e. it's still biased when your numbers are
in range 0 .. 1 for example).

See:
http://en.wikipedia.org/wiki/Banker's_rounding#Round-to-even_method

Joost.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top