Hi all !
I think you show below that the floating point standard is not the cause
of the faulty result, but that comparison tolerance is. JWithATwist uses
the same IEEE-754 floating point numbers and as we can see does not give
this faulty result.
From:
http://code.jsoftware.com/wiki/Essays/Tolerant_Comparison#Tolerant_Floor_and_Ceiling
"The algorithm used by J (see tfloor and tceiling below) starts with the
integer nearest to a, rounding up if 0.5 (=!.0) 1|a. Then, to obtain the
floor of a, we subtract one if the result is tolerantly greater than a.
Since the rounded number was within 0.5 of a, the smallest this number
can be is a - 0.5. Similarly, for the ceiling, we add one if it is
tolerantly less than a."
9!:19 ''
|rank error
From:
http://code.jsoftware.com/wiki/Essays/Tolerant_Comparison#Tolerant_Floor_and_Ceiling
"The tolerance is normally 2^_44"
2^_44
5.68434e_14
(2^_44 )*10^14
5.68434
To do Floor, J takes 100000000000000.500, rounds it to
100000000000001.000. This is not tolerantly greater than
100000000000000.500, since CT is 5.7, 1 is then not subtracted, as far
as I can understand. It seems very peculiar.
What I think this shows is that we also have to consider comparison
tolerance in our discussion concerning automatic type conversions. When
the programmer can not control the types it is hard for him to control
the effects of comparison tolerance in calculations?
/Erling
On 2016-08-22 14:43, Raul Miller wrote:
This happens because J is using 64 bit IEEE-754 floating point numbers.
See https://en.wikipedia.org/wiki/IEEE_floating_point for an overview.
This loss of precision is specified as a part of that standard.
More specifically, these numbers can be approximated as the expression:
s*m+2^e
where
s is _1 or 1
m is in the range 0 .. 2^53
e is in the range _1022 .. 1023
This standard is used because support for these numbers is baked into
the hardware. (Literally millions of transistors have been laid down
in your computer to implement these numbers.)
Even more specifically:
fc=:3!:5
ieee754=:3 :'(1 j.;1{.&.>~-1 11 52)#''01''{~,}.#:255,a.i.|.2 fc y'
ieee754 0
0 00000000000 0000000000000000000000000000000000000000000000000000
ieee754 1
0 01111111111 0000000000000000000000000000000000000000000000000000
ieee754 2
0 10000000000 0000000000000000000000000000000000000000000000000000
ieee754 3
0 10000000000 1000000000000000000000000000000000000000000000000000
ieee754 5
0 10000000001 0100000000000000000000000000000000000000000000000000
ieee754 7
0 10000000001 1100000000000000000000000000000000000000000000000000
Note that the very first bit of m is omitted from the representation
(because except for zero, every number has that bit and it is not
necessary to state it - and zero can be recognized by an all zero bit
pattern).
Also note that e is specified using an offset.
#. 0 1 1 1 1 1 1 1 1 1 1
1023
Subtract 1023 from the integer represented by the bit pattern to get
the intended value of e.
Finally, note that m is interpreted as a binary fraction in the range
0 .. 1 and with that implied leading 1, the denominator has an implied
value of
#.1,52#0
4503599627370496
So we can extract the implied values from the floating point representation:
s_e_m=: 3 :0
b=. , }. #: 255, a.i. |. 2 fc y
s=: _1^ {. b
e=: _1023+ #. 11{. }. b
m=: (#. 1, 12}. b) % 2^52x
s,e,m
)
s_e_m 1
1 0 1.00000000000000000
s_e_m 2
1 1 1.00000000000000000
s_e_m 3
1 1 1.50000000000000000
With that out of the way, we are ready to start looking at your example:
ieee754 10^14
0 10000101101 0110101111001100010000011110100100000000000000000000
ieee754 0.5+10^14
0 10000101101 0110101111001100010000011110100100000000000000100000
ieee754 <.0.5+10^14
0 10000101101 0110101111001100010000011110100100000000000001000000
Here, we are seeing the same thing as before, but in bit patterns,
rather than in numbers formatted for display. We can see that <. is
increasing the size of the value, but we don't quite understand yet
why that would be.
So here's another view:
s_e_m 10^14
1 46 1.42108547152020037
s_e_m 0.5+10^14
1 46 1.42108547152020748
s_e_m <.0.5+10^14
1 46 1.42108547152021458
We are seeing the same thing again, but now we have something that
might remind us of an epsilon mentioned in the dictionary:
http://www.jsoftware.com/help/dictionary/dx009.htm
9!:18 y
9!:19 y Comparison Tolerance. Queries and sets the comparison
tolerance. See Equal (=). The tolerance in particular cases can be set
using fit, thus: =!.t .
We could work through the math of that, or we could just try it out:
s_e_m (<.!.0)0.5+10^14
1 46 1.42108547152020037
That looks like the values for 10^14, which I think is what you were expecting.
So, you just need to use <.!.0 instead of <. and that will fix the
problem for you.
Or, if you don't like using !.0 all over the place:
9!:19]0
<.0.5+10^14
100000000000000
(But don't blame me if 9!:19]0 breaks some other obscure parts of the
system - you should maybe think about putting that value back to what
it was when you are done. And if you forgot to check before you set
it, and do not feel like restarting J, you can read
http://www.jsoftware.com/help/dictionary/d000.htm which will tell you
what it was.)
Thanks,
----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm