This is the mail archive of the kawa@sourceware.org mailing list for the Kawa project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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




Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]