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
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