Acknowledged
CIQQA-3581

Casting Float to Number does not yield Number

This code:

var myFloat = 5.0;
var myNumber = myFloat as Number;
var mod = myNumber % 1;

Compiles without warnings, but crashes at runtime with:

Error: Unhandled Exception
Exception: UnexpectedTypeException: Expected Number/Long, given Number/Float

As far as I can tell:

  • this behavior is undocumented
  • the types "Number/Long" and "Number/Float" are undocumented
  • the effects of casting numeric types are undocumented

Consequently I'm not sure how to characterize this bug. If it's expected, this is a documentation bug. If it's not expected, this is a runtime and/or compiler bug.

I read the relevant section on "type casting" in the SDK docs. I've reproduced the totality of the documentation on type casting here:

The as keyword can also be used in an expression to type cast a value to another type. This can be useful if the type is not clear to the type system.

This, unfortunately, does not help very much.

---

Tested on Descent G1 & G2 simulators

SDK 8.3.0

Parents
  • > Underflow, overflow

    I think these are handled by wrapping around (like C), rather than throwing a catchable exception or raising a fatal error.

    > arithmetic

    > promotion 

    Yes, there's various implicit conversions/promotions when you do arithmetic, but they're not really documented.

    - division: if one of the operands is a Float or Double, then floating-point division is performed

    - integer division: if both operands are integers, then integer division is performed

    - comparisons: numeric types are automatically converted/promoted as necessary so that comparisons with <, >, ==, ... work as expected

    Some things that may be unexpected:

    - Adding 2 Chars with "+" results in a String: 'a' + 'b' => "ab" 

    - Adding Char and Number/Long results in incrementing the Char: 'a' + 1 => 'b'

    - Null (or any other non-Boolean type) cannot be implicitly coerced to Boolean. This will be a compile-time error. If you disable the type checker or cast the value to Boolean, it will compile, but there may be unexpected effects at runtime, including crashes.

    > While numeric types are objects, there also seems to be no operator overloading. Correct?

    Correct.

    Another thing to note is that while Long and Double are immutable value types like the other "primitive" types (i.e. Boolean, Number, Float, String, Null), unlike Number and Float, they are allocated on the heap and not the stack, which means they have a huge memory penalty which adds up. So it's best to avoid using Long or Double in an array with many elements unless you really need to.

    (The docs hilariously once said Monkey C doesn't have primitive types, because everything is an object, but then it referred to primitives anyway, all in one sentence. So you can also think of them as the immutable value types. Even though they are Objects, they have much less memory overhead than other objects in Monkey C)

    - Stack: Boolean, Number, Float, Null.

    These values take up 5 bytes of memory. (1 byte for type, 32 bits for value)

    - Heap: Long, Double, String

    Long and Double take up 13 bytes of memory. (1 byte for type, 4 bytes for pointer, 64 bits for value)

    - Heap: Array, Dictionary

    These have about ~13 bytes of memory overhead above and beyond their contents. Dictionaries are more expensive than arrays in general, because there's significant overhead for each key/value pair

    - Heap: other Objects

    Objects have a huge memory overhead of ~35 bytes, which makes them very expensive even compared to Arrays and Dictionaries

    --

    Since there's no comprehensive language reference, unfortunately the only way to discover a lot of this stuff is to find out on your own, or hope someone else posted about it.

Comment
  • > Underflow, overflow

    I think these are handled by wrapping around (like C), rather than throwing a catchable exception or raising a fatal error.

    > arithmetic

    > promotion 

    Yes, there's various implicit conversions/promotions when you do arithmetic, but they're not really documented.

    - division: if one of the operands is a Float or Double, then floating-point division is performed

    - integer division: if both operands are integers, then integer division is performed

    - comparisons: numeric types are automatically converted/promoted as necessary so that comparisons with <, >, ==, ... work as expected

    Some things that may be unexpected:

    - Adding 2 Chars with "+" results in a String: 'a' + 'b' => "ab" 

    - Adding Char and Number/Long results in incrementing the Char: 'a' + 1 => 'b'

    - Null (or any other non-Boolean type) cannot be implicitly coerced to Boolean. This will be a compile-time error. If you disable the type checker or cast the value to Boolean, it will compile, but there may be unexpected effects at runtime, including crashes.

    > While numeric types are objects, there also seems to be no operator overloading. Correct?

    Correct.

    Another thing to note is that while Long and Double are immutable value types like the other "primitive" types (i.e. Boolean, Number, Float, String, Null), unlike Number and Float, they are allocated on the heap and not the stack, which means they have a huge memory penalty which adds up. So it's best to avoid using Long or Double in an array with many elements unless you really need to.

    (The docs hilariously once said Monkey C doesn't have primitive types, because everything is an object, but then it referred to primitives anyway, all in one sentence. So you can also think of them as the immutable value types. Even though they are Objects, they have much less memory overhead than other objects in Monkey C)

    - Stack: Boolean, Number, Float, Null.

    These values take up 5 bytes of memory. (1 byte for type, 32 bits for value)

    - Heap: Long, Double, String

    Long and Double take up 13 bytes of memory. (1 byte for type, 4 bytes for pointer, 64 bits for value)

    - Heap: Array, Dictionary

    These have about ~13 bytes of memory overhead above and beyond their contents. Dictionaries are more expensive than arrays in general, because there's significant overhead for each key/value pair

    - Heap: other Objects

    Objects have a huge memory overhead of ~35 bytes, which makes them very expensive even compared to Arrays and Dictionaries

    --

    Since there's no comprehensive language reference, unfortunately the only way to discover a lot of this stuff is to find out on your own, or hope someone else posted about it.

Children
No Data