diff --git a/compiler/examples/mandelbrot.p8 b/compiler/examples/mandelbrot.p8 index 9cf254fff..b858e592f 100644 --- a/compiler/examples/mandelbrot.p8 +++ b/compiler/examples/mandelbrot.p8 @@ -25,11 +25,9 @@ flt(44) for pixely in yoffset to yoffset+height-1 { - ; yy = (pixely-yoffset)/height/3.6+0.4 ; @todo compiler float error yy = flt((pixely-yoffset))/height/3.6+0.4 for pixelx in xoffset to xoffset+width-1 { - ; xx = (pixelx-xoffset)/width/3+0.2 ; @todo compiler float error xx = flt((pixelx-xoffset))/width/3+0.2 x = 0.0 diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index b120a6e0d..faac9d7d8 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -4,41 +4,26 @@ sub start() -> () { - byte i=2 - float f - word ww = $55aa + byte i - set_carry() - clear_carry() - set_irqd() - clear_irqd() - - f=flt(i) - i = msb(ww) - i = lsb(ww) - lsl(i) - lsr(i) - rol(i) - ror(i) - rol2(i) - ror2(i) + float yy + word pixely + word yoffset + word height - - while i<10 { - _vm_write_num(i) - _vm_write_char($8d) - i++ - } - - _vm_write_char($8d) - - i=2 - repeat { - _vm_write_num(i) - _vm_write_char($8d) - i++ - } until i>10 + ; @todo expression must not result in float but in word + yy = flt(height+1.1) + pixely = height / 100 + ;yy = height- 1.1 + ;yy = height*1.1 + ;yy = height/3.6 + ;yy = height//3.6 + ;yy = height**3.6 + ;yy = height%3.6 + ;yy = height/3.6+0.4 + ;yy = 2/height/3.6+0.4 + ;yy = (pixely-yoffset)/height/3.6+0.4 } } diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index ca337e5b4..587a3c3f9 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -725,8 +725,7 @@ class BinaryExpression(var left: IExpression, val operator: String, var right: I val leftDt = left.resultingDatatype(namespace) val rightDt = right.resultingDatatype(namespace) return when(operator) { - "+", "-", "*", "**", "%" -> if(leftDt==null || rightDt==null) null else arithmeticOpDt(leftDt, rightDt) - "/" -> if(leftDt==null || rightDt==null) null else DataType.FLOAT + "+", "-", "*", "**", "/", "%" -> if(leftDt==null || rightDt==null) null else arithmeticOpDt(leftDt, rightDt) "//" -> if(leftDt==null || rightDt==null) null else integerDivisionOpDt(leftDt, rightDt) "&" -> leftDt "|" -> leftDt diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 85cec62ba..2ca079cfc 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -722,7 +722,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions: if(sourceDatatype==DataType.WORD && targetDatatype==DataType.BYTE) checkResult.add(ExpressionError("cannot assign word to byte, use msb() or lsb()?", position)) else if(sourceDatatype==DataType.FLOAT && (targetDatatype==DataType.BYTE || targetDatatype==DataType.WORD)) - checkResult.add(ExpressionError("cannot assign ${sourceDatatype.toString().toLowerCase()} to ${targetDatatype.toString().toLowerCase()}; possible loss of precision", position)) + checkResult.add(ExpressionError("cannot assign float to ${targetDatatype.toString().toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)) else checkResult.add(ExpressionError("cannot assign ${sourceDatatype.toString().toLowerCase()} to ${targetDatatype.toString().toLowerCase()}", position)) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 7a9f6a774..123d0f81f 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -289,6 +289,15 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva stackvmProg.instr(Opcode.NOP) } + private fun checkForFloatPrecisionProblem(left: IExpression, right: IExpression) { + val leftDt = left.resultingDatatype(namespace) + val rightDt = right.resultingDatatype(namespace) + if (leftDt == DataType.BYTE || leftDt == DataType.WORD) { + if(rightDt==DataType.FLOAT) + printWarning("byte or word value implicitly converted to float. Suggestion: use explicit flt() conversion or revert to integer arithmetic", left.position) + } + } + private fun translate(expr: IExpression) { when(expr) { is RegisterExpr -> { @@ -299,6 +308,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva translatePrefixOperator(expr.operator) } is BinaryExpression -> { + checkForFloatPrecisionProblem(expr.left, expr.right) translate(expr.left) translate(expr.right) translateBinaryOperator(expr.operator) diff --git a/compiler/src/prog8/optimizing/Extensions.kt b/compiler/src/prog8/optimizing/Extensions.kt index ccc344d49..7705b768a 100644 --- a/compiler/src/prog8/optimizing/Extensions.kt +++ b/compiler/src/prog8/optimizing/Extensions.kt @@ -7,7 +7,7 @@ import prog8.ast.Module import prog8.parser.ParsingFailedError /** - * TODO: array, matrix, string and float constants should be put into a constant-pool + * TODO: array, matrix, string and float constants should be put into a constant-pool, * so that they're only stored once instead of replicated everywhere. * Note that initial constant folding of them is fine: it's needed to be able to * optimize the expressions. But as a final step, they should be consolidated again diff --git a/compiler/test/StackVMOpcodeTests.kt b/compiler/test/StackVMOpcodeTests.kt index 55a9f4bf0..e874066ac 100644 --- a/compiler/test/StackVMOpcodeTests.kt +++ b/compiler/test/StackVMOpcodeTests.kt @@ -1178,7 +1178,7 @@ class TestStackVmOpcodes { @Test fun testReturn() { - // @todo only tests return with zero return values + // @todo this only tests return with zero return values for now. val ins = mutableListOf( Instruction(Opcode.RETURN), Instruction(Opcode.TERMINATE), @@ -1199,7 +1199,7 @@ class TestStackVmOpcodes { @Test fun testCall() { - // @todo only tests call with zero parameters + // @todo this only tests call with zero parameters for now. val ins = mutableListOf( Instruction(Opcode.CALL, callLabel = "label"), Instruction(Opcode.LINE, Value(DataType.STR, null, stringvalue = "returned")), diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 493dda0f3..627bd3e51 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -372,7 +372,7 @@ for normal assignments (``A = A + X``). The reverse is *not* true: it is *not* possible to assign a value of a 'larger' datatype to a variable of a smaller datatype without an explicit conversion. Otherwise you'll get an error telling you that there is a loss of precision. You can use builtin functions such as ``round`` and ``lsb`` to convert - to a smaller datatype. + to a smaller datatype, or revert to integer arithmetic. Expressions ----------- @@ -385,13 +385,18 @@ There are various built-in functions such as sin(), cos(), min(), max() that can You can also reference idendifiers defined elsewhere in your code. .. attention:: - **Data type conversion (during calculations):** + **Data type conversion (during calculations) and floating point handling:** + BYTE values used in arithmetic expressions (calculations) will be automatically converted into WORD values if the calculation needs that to store the resulting value. Once a WORD value is used, all other results will be WORDs as well (there's no automatic conversion of WORD into BYTE). - *There is never an automatic conversion into floating point values, and the compiler will NOT issue a warning for this.* - If you require float precision, you'll have to first convert into a floating point explicitly using the ``flt`` builtin function. - For example, this means that if you divide two integer values (say: ``32500 / 99``) the result will be the integer floor + + When a floating point value is used in a calculation, the result will be a floating point, and byte or word values + will be automatically converted into floats in this case. The compiler will issue a warning though when this happens, because floating + point calculations are very slow and possibly unintended! + + Calculations with integers will not result in floating point values; + if you divide two integer values (say: ``32500 / 99``) the result will be the integer floor division (328) rather than the floating point result (328.2828282828283). If you need the full precision, you'll have to write ``flt(32500) / 99`` (or if they're constants, simply ``32500.0 / 99``), to make sure the first operand is a floating point value.