Ah, ok... that deals with the NaN error. But it still doesn't seem to do anything to resolve the problem:
round=: [ * [: <. 0.5 + %~ MAX =: ->: MIN =: _2 <.@^ 63 1 round ([: <. MIN>. MAX<.]) 1 round 0.5 + 50.4 2e10 1e30 _ 51 2e10 9.22337e18 9.22337e18 3!:0 ] 1 round ([: <. MIN>. MAX<.]) 1 round 0.5 + 50.4 2e10 1e30 _ 8 vs. ([: <. MIN>. MAX<.])&.x:@> 0.5 + 50.4 2e10 1e30 _ 50 20000000000 9223372036854775807 9223372036854775807 3!:0 ([: <. MIN>. MAX<.])&.x:@> 0.5 + 50.4 2e10 1e30 _ 4 (And, as suggested earlier in this thread, you can omit the @> if you are not dealing with a large array (I like to use &> instead of "0 where the value to its right is numeric). And, I guess, you could also instead do something typically more efficient than @>. For, replace @> with (blocked) where blocked=: 1 :'[: ; _1e6 <@u\ ]' - since the cost of breaking up the computation isn't paid for every single number, this approach mostly just adds a small constant cost to the total computation time.) Thanks, -- Raul On Thu, Jan 14, 2016 at 5:48 PM, Kip Murray <[email protected]> wrote: > Here is the description of verb round: > > NB.*round v round y to nearest x (e.g. 1000 round 12345) > round=: [ * [: <. 0.5 + %~ > > > On Thu, Jan 14, 2016 at 3:41 PM, Raul Miller <[email protected]> wrote: > >> Did you try it? It seems to me that this just makes things worse: >> >> round=: [ * [: <. 0.5 + %~ >> round 0.5 + 50.4 2e10 1e30 _ >> |NaN error: round >> >> Thanks, >> >> -- >> Raul >> >> On Thu, Jan 14, 2016 at 4:28 PM, Kip Murray <[email protected]> >> wrote: >> > Try >> > >> > round >> > [ * [: <. 0.5 + %~ >> > >> > which I found in j602. >> > >> > --Kip Murray >> > >> > On Thu, Jan 14, 2016 at 1:56 PM, Marshall Lochbaum <[email protected] >> > >> > wrote: >> > >> >> Here's something I spent far too long on, and consequently thought was >> >> worth sharing. I can turn it into an essay on the J wiki if people want >> >> that. >> >> >> >> Recently I ran into the problem of rounding a J floating point number to >> >> an integer, and forcing the result to have integer type. This seems like >> >> a simple task: using a standard rounding function, we have >> >> 0.5 <.@:+ 2.3 5.1 7.6 3.9 >> >> 2 5 8 4 >> >> But with numbers that are too large, the result still contains >> >> floating-point numbers, and has type 8 (floating point) rather than 4 >> >> (integer). >> >> 0.5 <.@:+ _1e50 2e30 _ >> >> _1e50 2e30 _ >> >> 3!:0 ]0.5 <.@:+ _1e50 2e30 _ >> >> 8 >> >> When applied to a float, (<.) applies the C floor function, which yields >> >> another float, and than casts the results to integers if all of them are >> >> exactly representable as integers. They're not here, so they are left as >> >> floating-point numbers. >> >> >> >> To give a more accurate problem statement, I want the 64-bit signed >> >> integer which is closest to the function input. Thus numbers above the >> >> maximum representable integer should round to that integer, and likewise >> >> for numbers below the minimum representable integer. We define these two >> >> bounds now. >> >> MAX =: ->: MIN =: _2 <.@^ 63 >> >> Note that since MAX is one less than 2^63, trying to take (2<.@^63) >> >> would give us a float, and subtracting one would still leave us with a >> >> floating point number, which is not actually equal to MAX since (>:MAX) >> >> is representable as a float, while nearby integers are not. MIN on the >> >> other hand is safely computed as an exponent. Note the negative base, >> >> which works because 63 is odd. >> >> >> >> With these bounds, our problem should be easy: clamp to the integer >> >> range, then use (<.). >> >> ([: <. MIN>.MAX<.]) 0.5 + __ _1e30 _1e10 _100.3 >> >> _9223372036854775808 _9223372036854775808 _10000000000 _100 >> >> So far, so good... >> >> ([: <. MIN>.MAX<.]) 0.5 + 50.4 2e10 1e30 _ >> >> 50 2e10 9.22337e18 9.22337e18 >> >> Oops. What happened? >> >> MAX <. _ >> >> 9.22337e18 >> >> Since one of the arguments is a float, (<.) casts both to floats, and >> >> takes the minimum. But the closest floating-point number to MAX is >> >> (MAX+1), and that number's floor (MAX+1) isn't representable as an >> >> integer--it's one too big. We didn't have this problem with MIN, since >> >> it is exactly a negative power of two. >> >> <. MAX+1 >> >> 9.22337e18 >> >> >> >> We'll make a test case that contains numbers close to both bounds. I've >> >> included the addition of 0.5 in t so I can focus on the floor function >> >> from now on. The type of our result is a float, so we failed. >> >> t =. 0.5 + __,_,~ (MIN,0,MAX) +/(,@:) i:1e5 >> >> 3!:0 ([: <. MIN>.MAX<.]) t >> >> 8 >> >> >> >> We can fix the problem by using exact integers, but it's extremely slow. >> >> However, it serves as a good answer key. The ("0) is there for a >> >> reason--otherwise the big array of exact integers tends to flood RAM. >> >> fl_e =: (MIN>.MAX<.<.)&.:x:"0 NB. exact floor >> >> 3!:0 key =. fl_e t >> >> 4 >> >> 10 (6!:2) 'fl_e t' >> >> 3.62018 >> >> >> >> If we use a number small enough that its floating-point representation >> >> is equal to a 64-bit integer, then we can force our answer to be a >> >> float, but it's not correct since the results are sometimes too small. >> >> If that doesn't matter and speed is critical, this is the right method >> >> to use. >> >> MAX1 =. MAX - 512 >> >> fl_f =: [: <. MIN>.MAX1<.] NB. fast floor >> >> 3!:0 fl_f t >> >> 4 >> >> key -: fl_f t >> >> 0 >> >> 10 (6!:2) 'fl_f t' >> >> 0.0179205 >> >> >> >> Finally, my solution. It's not particularly elegant, but it is correct >> >> and has good performance. We reduce all the values larger than MAX to >> >> zero, then clamp on the minimum side and take the floor. For the values >> >> that we removed, we add MAX back in. The comparison (<:&MAX) is only >> >> computed once to save a little time. >> >> fl =: ((MAX*-.@]) + [: <. MIN>.*) <:&MAX >> >> key -: fl t >> >> 1 >> >> 10 (6!:2) 'fl t' >> >> 0.0426181 >> >> It's critical to use (<:) rather than (<) to test whether numbers are >> >> acceptable even though it fails MAX, which wouldn't break (<.). That's >> >> because comparisons cast their arguments to floats before comparing, so >> >> MAX < MAX+1 >> >> 0 >> >> >> >> Maybe there's a quicker solution to be found. The following rounds >> >> towards zero quickly by negating all the positive numbers, and restoring >> >> their signs later. However, adding in the cases to make it equal to (<.) >> >> on small numbers removes its advantage. >> >> fl_o =: (] * MIN <.@:>. *) -@:* NB. floor towards zero >> >> fl_o _4.6 _3 _2.8 _1.2 3.4 5.8 9 >> >> _5 _3 _3 _2 4 6 9 >> >> 10 (6!:2) 'fl_o t' >> >> 0.0324293 >> >> >> >> Any takers? >> >> >> >> Marshall >> >> ---------------------------------------------------------------------- >> >> For information about J forums see http://www.jsoftware.com/forums.htm >> > ---------------------------------------------------------------------- >> > For information about J forums see http://www.jsoftware.com/forums.htm >> ---------------------------------------------------------------------- >> For information about J forums see http://www.jsoftware.com/forums.htm >> > ---------------------------------------------------------------------- > For information about J forums see http://www.jsoftware.com/forums.htm ---------------------------------------------------------------------- For information about J forums see http://www.jsoftware.com/forums.htm
