lua_number2integer

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

lua_number2integer

Cuero Bugot
Hi all,

I have seen that there are already a few posts on that subject on the list archives, however I did not find a conclusion.

The fact is that the function lua_number2integer has a behavior which is really too random, depending on which platform lua is running. The header of the macro specify indeed that the rounding method is not defined. In my opinion this lead to an unacceptable portability issue.

Here is the code which revealed the unconsistency of the number to integer process:

    string.char(112+remaining/256, remaining%256)

where remaining was taken from 0 to 4095.


This code was done assuming that the string.char function was "correctly truncating" the value as C would do it in such a case.

To enlight the problem:
Here is what I get on Ubuntu 8.04 32 bits:
Lua 5.1.2  Copyright (C) 1994-2007 Lua.org, PUC-Rio
> = string.char(72)
H
> = string.char(73)
I
> = string.char(72.1)
H
> =  string.char(72.5)
H
> = string.char(72.51)
I
> = string.char(72.5001)
H
>
Here is what I get on Windows XP 32 bits, (lua compiled with MSVC)
Lua 5.1.3  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> = string.char(72)
H
> = string.char(73)
I
> = string.char(72.1)
H
> =  string.char(72.5)
H
> = string.char(72.51)
I
> = string.char(72.5001)
I
>

Any comments ?

Regards,

Cuero
--

Anyware Technologies
Cuero Bugot

[hidden email]
Tel : +33(0)5 61 00 06 53
Fax : +33(0)5 61 00 51 46
 
Nouvelle adresse
Anyware Technologies
Lake Park
ZAC de l'Hers - Allée du Lac
Bâtiment A - Rdc - BP 87216
31672 Labège Cedex
France
www.anyware-tech.com
Reply | Threaded
Open this post in threaded view
|

Re: lua_number2integer

Mike Pall-89
Cuero Bugot wrote:
> Here is the code which revealed the unconsistency of the number to integer 
> process:
>
>    string.char(112+remaining/256, remaining%256)
>
> where remaining was taken from 0 to 4095.
>
> This code was done assuming that the string.char function was "correctly 
> truncating" the value as C would do it in such a case.

Passing non-integral numbers to library functions expecting
integral numbers is "undefined". There is no "correct rounding
mode" in this case -- Lua is not C.

Rewrite your example as:

  local r = remaining % 256
  string.char(112 + (remaining-r)/256, r)

Or better use the struct library:

  struct.pack(">H", 0x7000+remaining)

Get it from: http://www.inf.puc-rio.br/~roberto/struct/

--Mike

Reply | Threaded
Open this post in threaded view
|

Re: lua_number2integer

David Given
Mike Pall wrote:
[...]
>   local r = remaining % 256
>   string.char(112 + (remaining-r)/256, r)

Is this the preferred approach to using math.floor() (which is what I
usually end up with when converting things to integers)?

-- 
David Given
[hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: lua_number2integer

Peter Cawley
It is a more efficient way of doing it, so long as you want to keep
the remainder. It may well be more efficient even if you don't want to
keep the remainder, but I'm less sure of that.

Peter

2008/9/25 David Given <[hidden email]>:
> Mike Pall wrote:
> [...]
>>   local r = remaining % 256
>>   string.char(112 + (remaining-r)/256, r)
>
> Is this the preferred approach to using math.floor() (which is what I
> usually end up with when converting things to integers)?
>
> --
> David Given
> [hidden email]
>

Reply | Threaded
Open this post in threaded view
|

Re: lua_number2integer

Mike Pall-89
In reply to this post by David Given
David Given wrote:
> Mike Pall wrote:
> >   local r = remaining % 256
> >   string.char(112 + (remaining-r)/256, r)
> 
> Is this the preferred approach to using math.floor() (which is what I
> usually end up with when converting things to integers)?

Sure, at least if you need both the remainder and the result of
floor-division. If you only need the latter, then math.floor(a/b)
is probably more common.

If you want to know about performance (lower numbers are better):

Time in seconds for | Lua    | LuaJIT |
1e8 iterations of:  | 5.1.4  | 1.1.4  |
--------------------+--------+--------+
r=a%b               |  2.09  |  0.70  |
d=floor(a/b)        |  6.22  |  1.10  |
r=a%b; d=(a-r)/b    |  3.76  |  0.84  |
r=a%b; d=floor(a/b) |  7.97  |  1.44  |

[Using locals and local floor = math.floor of course.]

Apparently the call overhead for floor() is the main factor here
because the '%' operator also uses floor() (the inlined C library
function).

Well ... maybe Lua needs a floor-division operator '//'?

--Mike

Reply | Threaded
Open this post in threaded view
|

Re: lua_number2integer

David Given
Mike Pall wrote:
> r=a%b; d=(a-r)/b    |  3.76  |  0.84  |

Is LuaJIT intelligent enough to spot a % and / near each other and
optimise them into a single divide-with-remainder operation?

[...]
> Well ... maybe Lua needs a floor-division operator '//'?

It is the *one* thing that's missing from a all-numbers-are-doubles
system; all the other arithmetic operations are fundamentally the same
for both types, but division isn't.

-- 
David Given
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: lua_number2integer

Tony Finch
In reply to this post by Mike Pall-89
On Thu, 25 Sep 2008, Mike Pall wrote:
>
> Apparently the call overhead for floor() is the main factor here
> because the '%' operator also uses floor() (the inlined C library
> function).

It is usually not inlined. As far as I can tell this is because it has
caused too many optimizer bugs in the past. The implementation uses really
nasty fiddling of the FP rounding mode - it's surprisingly non-trivial.

Tony.
-- 
f.anthony.n.finch  <[hidden email]>  http://dotat.at/
ROCKALL MALIN HEBRIDES: SOUTHERLY 5 TO 7, INCREASING GALE 8, PERHAPS SEVERE
GALE 9, LATER IN ROCKALL AND HEBRIDES. MODERATE OR ROUGH, INCREASING VERY
ROUGH OR HIGH LATER IN ROCKALL AND HEBRIDES. OCCASIONAL RAIN. MODERATE OR
GOOD, OCCASIONALLY POOR IN ROCKALL AND HEBRIDES.

Reply | Threaded
Open this post in threaded view
|

Re: lua_number2integer

Asko Kauppi
In reply to this post by Mike Pall-89

'lua_tointeger()' could easily be changed so that it does not allow non-integer numbers as input.

The LNUM patch actually does this, unless LUA_COMPAT_TOINTEGER is defined.


LUA_API lua_Integer lua_tointeger (lua_State *L, int idx) {
  TValue n;
/* Lua 5.1 documented behaviour is to return nonzero for non- integer: * "If the number is not an integer, it is truncated in some non- specified way."
     */
#ifdef LUA_COMPAT_TOINTEGER
	...
#else
  /* New suggestion */
  const TValue *o = index2adr(L, idx);
  if (tonumber(o, &n)) {
    lua_Integer i;
    if (ttisint(o)) return ivalue(o);
    if (tt_integer_valued(o,&i)) return i;
  }
#endif
  return 0;
}


Also, a 'lua_isinteger()' would be needed to tell, if a value can be carried unchanged in 'lua_Integer'.


LUA_API int lua_isinteger (lua_State *L, int idx) {
  TValue tmp;
  lua_Integer dum;
  const TValue *o = index2adr(L, idx);
#ifdef LUA_TINT
  return tonumber(o,&tmp) && (ttisint(o) || tt_integer_valued(o,&dum));
#else
  return tonumber(o,&tmp) && tt_integer_valued(o,&dum);
#endif
}

-asko


Mike Pall kirjoitti 25.9.2008 kello 15:56:

Cuero Bugot wrote:
Here is the code which revealed the unconsistency of the number to integer
process:

  string.char(112+remaining/256, remaining%256)

where remaining was taken from 0 to 4095.

This code was done assuming that the string.char function was "correctly
truncating" the value as C would do it in such a case.

Passing non-integral numbers to library functions expecting
integral numbers is "undefined". There is no "correct rounding
mode" in this case -- Lua is not C.

Rewrite your example as:

 local r = remaining % 256
 string.char(112 + (remaining-r)/256, r)

Or better use the struct library:

 struct.pack(">H", 0x7000+remaining)

Get it from: http://www.inf.puc-rio.br/~roberto/struct/

--Mike


Reply | Threaded
Open this post in threaded view
|

Re: lua_number2integer

Mike Pall-89
In reply to this post by David Given
David Given wrote:
> Mike Pall wrote:
> > r=a%b; d=(a-r)/b    |  3.76  |  0.84  |
> 
> Is LuaJIT intelligent enough to spot a % and / near each other and
> optimise them into a single divide-with-remainder operation?

LuaJIT 2.x does that, but LuaJIT 1.x doesn't.

Since I had no explanation for this low timing myself, I've
experimented with different dividends and divisors and found some
cases where this particular timing goes up to 1.41 (for two
divides per iteration). This is more in line with the documented
worst-case scalar division throughput for a 45nm Core2:
  1e8 iterations at 0.7s = 7ns/iteration = 21 cycles at 3 GHz

Looks like this new-fangled CPU is pretty clever at optimizing
divides with a low number of one-bits:

  http://www.hardwaresecrets.com/article/434/4

And the SSE4.1 roundsd is around ten times (!) faster than the x87
control-word juggling and frndint required to implement floor(). :-)
Will definitely use this in LJ2 (based on CPU feature-detection of
course).

> > Well ... maybe Lua needs a floor-division operator '//'?
> 
> It is the *one* thing that's missing from a all-numbers-are-doubles
> system; all the other arithmetic operations are fundamentally the same
> for both types, but division isn't.

Ask Roberto often enough and it may end up in 5.2. :-)

--Mike