This is the mail archive of the
kawa@sourceware.org
mailing list for the Kawa project.
Re: Numbers in Kawa (was: GSOC | Extending Common Lisp support)
On Apr 27, 2012, at 12:24 PM, Helmut Eller wrote:
It's also in line with tradition to
emit a warning at compile time but still generate code that will
raise
an runtime error. IMO Kawa acts quite gracefully here.
A different story is this example:
(define (test)
(let ((x :: int 10.5))
(* x x)))
Kawa returns 110 which seems quite wrong.
The difference here with int instead of integer is of course due to
how coercions to primitive types are handled [and (*:intValue 110.25)
returns 110]. It does seem like things should be more consistent
between primitive and Object numbers.
But I don't really see a return value of 110 as being wrong, per
se: the
program itself is flawed, since 10.5 is not an int, so we're
already in
nasal demon territory, but 110 is the nearest int to (* 10.5 10.5),
so
the result seems fairly gracious. What would you have expected?
I'd expect a compile time warning and an error at runtime. Automagic
coercion from float to int surprises me. E.g. (string-ref "abc" 0.6)
should be an error instead of returning #\a or #\b.
That does seem erroneous. I feel like (string-ref "abc" 1.0) should
work,
though [with a warning], so perhaps it should be an error iff the float
isn't already integer-valued?
One could also argue that either 11 or 10 is the nearest integer to
10.5
so the result should either be 121 or 100. 110 is quite odd, as the
coercion happens after the multiplication, but the type declaration is
lexically before the multiplication.
That's true. It's not obvious that (let ((x :: int 10.5)) (* x x)) is
different from (let ((x (as int 10.5))) (* x x)). Both issue a warning
"saw float where int expected" but the latter returns 100 because it
actually performs int multiplication.
I will note that the compiler has taken the extra step to infer
that if
x is an int, then (* x x) will be an int, too. That would not be a
valid
inference in Common Lisp.
AFAIU, int in Kawa is intentionally not the same as (signed-byte 32)
in
CL. It's a different type so that operations like +, * etc. can have
"methods" that wrap around. I think the boxed representation of an
int
is java.lang.Integer and the boxed representation of (unsigned-byte
32)
would be something like Fixnum or Bignum. The trouble is that
automatic
coercions from ints to other number types aren't that well specified.
Worse, the numeric conversions aren't consistent. IMHO if you can
convert
to an int, you should be able to convert to an integer, too.
#|kawa:1|# (as int 10.0)
/dev/stdin:1:9: warning - saw float where int expected
10
#|kawa:2|# (integer? 10.0)
#t
#|kawa:3|# (as integer 10.0)
/dev/stdin:3:13: warning - type DFloNum is incompatible with required
type integer
/dev/stdin:3:1: warning - cannot convert literal (of type
gnu.math.DFloNum) to Type integer
Value '10.0' has wrong type (DFloNum) (expected: integer)
at gnu.kawa.lispexpr.LangObjType.coerceIntNum(LangObjType.java:340)
at atInteractiveLevel$3.run(stdin:3)
at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:299)
at gnu.expr.ModuleExp.evalModule(ModuleExp.java:200)
at kawa.Shell.run(Shell.java:279)
at kawa.Shell.run(Shell.java:194)
at kawa.Shell.run(Shell.java:175)
at kawa.repl.main(repl.java:884)
Perhaps if DFloNum (or a superclass?) had a method like:
public boolean isInexactInteger()
{
return (0.0 == Math.IEEEremainder(doubleValue(), 1.0));
}
and IntNum's asIntNumOrNull(Object value) included a clause
if (value instanceof DFloNum && ((DFloNum)value).isInexactInteger())
return IntNum.make(((DFloNum) value).longValue());
[time passes...]
That seems to work:
#|kawa:1|# (as int 10.0)
/dev/stdin:1:9: warning - saw float where int expected
10
#|kawa:2|# (as integer 10.0)
/dev/stdin:2:13: warning - type DFloNum is incompatible with required
type integer
10
#|kawa:3|# (let ((x ::integer 10.0)) (* x x))
/dev/stdin:3:20: warning - type DFloNum is incompatible with required
type integer
100
#|kawa:4|# (let ((x ::integer 10.5)) (* x x))
/dev/stdin:4:20: warning - type DFloNum is incompatible with required
type integer
/dev/stdin:4:27: warning - cannot convert literal (of type
gnu.math.DFloNum) to Type integer
Value '110.25' has wrong type (DFloNum) (expected: integer)
at gnu.kawa.lispexpr.LangObjType.coerceIntNum(LangObjType.java:340)
at atInteractiveLevel$4.run(stdin:4)
at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:299)
at gnu.expr.ModuleExp.evalModule(ModuleExp.java:200)
at kawa.Shell.run(Shell.java:279)
at kawa.Shell.run(Shell.java:194)
at kawa.Shell.run(Shell.java:175)
at kawa.repl.main(repl.java:884)
But that still won't work with doubles or Doubles, so perhaps
the IEEEremainder test should be a static method of IntNum,
like
public static boolean isIntegerValued(Number value)
{
return (0.0 == Math.IEEEremainder(value.doubleValue(), 1.0));
}
and the test in asIntNumOrNull
if (value instanceof Number
&& (value instanceof Integer || ...
isIntegerValued((Number)value)))
Sorry for hijacking your thread, Charlie.
-J
--
Jamison Hope
The PTR Group
www.theptrgroup.com