Skip to content

Commit

Permalink
Fix conversion to from float to int
Browse files Browse the repository at this point in the history
Round the value.

The previous behavior is that

 - For the interpreter, we were rounding (same as new behavior)
 - for Rust and C++ we were truncating, unless the properties were
   inlinined and then we were keeping it as float
  • Loading branch information
ogoffart committed Jul 23, 2024
1 parent ef92c1a commit f5d003d
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 27 deletions.
8 changes: 5 additions & 3 deletions docs/reference/src/language/syntax/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,10 @@ conversions are allowed between some types for convenience.

The following conversions are possible:

- `int` can be converted implicitly to `float` and vice-versa
- `int` can be converted implicitly to `float` and vice-versa.
When converting from `float` to `int`, the value is rounded to the nearest integer.
- `int` and `float` can be converted implicitly to `string`
- `physical-length` and `length` can be converted implicitly to each other only in
- `physical-length`, relative-font-size, and `length` can be converted implicitly to each other only in
context where the pixel ratio is known.
- the units type (`length`, `physical-length`, `duration`, ...) can't be converted to numbers (`float` or `int`)
but they can be divided by themselves to result in a number. Similarly, a number can be multiplied by one of
Expand All @@ -297,7 +298,7 @@ The following conversions are possible:
- Struct types convert with another struct type if they have the same property names and their types can be converted.
The source struct can have either missing properties, or extra properties. But not both.
- Arrays generally don't convert between each other. Array literals can be converted if the element types are convertible.
- String can be converted to float by using the `to-float` function. That function returns 0 if the string isen't
- String can be converted to float by using the `to-float` function. That function returns 0 if the string isn't
a valid number. You can check with `is-float()` if the string contains a valid number

```slint,no-preview
Expand All @@ -314,5 +315,6 @@ export component Example {
property<string> xxx: "42.1";
property<float> xxx1: xxx.to-float(); // 42.1
property<bool> xxx2: xxx.is-float(); // true
property<int> xxx3: 45.8; // 46
}
```
2 changes: 1 addition & 1 deletion internal/compiler/builtin_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ fn rgb_macro(
op: '*',
}
} else {
expr.maybe_convert_to(Type::Int32, &n, diag)
expr.maybe_convert_to(Type::Float32, &n, diag)
}
} else {
expr.maybe_convert_to(Type::Float32, &n, diag)
Expand Down
25 changes: 5 additions & 20 deletions internal/compiler/generator/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,6 @@ use std::num::NonZeroUsize;
struct ConditionalIncludes {
iostream: Cell<bool>,
cstdlib: Cell<bool>,
cmath: Cell<bool>,
}

#[derive(Clone)]
Expand Down Expand Up @@ -545,6 +544,7 @@ pub fn generate(
file.includes.push("<array>".into());
file.includes.push("<limits>".into());
file.includes.push("<slint.h>".into());
file.includes.push("<cmath>".into());

for (path, er) in doc.embedded_file_resources.borrow().iter() {
match &er.kind {
Expand Down Expand Up @@ -879,10 +879,6 @@ pub fn generate(
file.includes.push("<cstdlib>".into());
}

if conditional_includes.cmath.get() {
file.includes.push("<cmath>".into());
}

file
}

Expand Down Expand Up @@ -2768,11 +2764,14 @@ fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String
Expression::Cast { from, to } => {
let f = compile_expression(from, ctx);
match (from.ty(ctx), to) {
(Type::Float32, Type::Int32) => {
format!("std::round({f})")
}
(from, Type::String) if from.as_unit_product().is_some() => {
format!("slint::SharedString::from_number({})", f)
}
(Type::Float32, Type::Model) | (Type::Int32, Type::Model) => {
format!("std::make_shared<slint::private_api::UIntModel>(std::max(0, {}))", f)
format!("std::make_shared<slint::private_api::UIntModel>(std::max<int>(0, {}))", f)
}
(Type::Array(_), Type::Model) => f,
(Type::Float32, Type::Color) => {
Expand Down Expand Up @@ -3131,59 +3130,45 @@ fn compile_builtin_function_call(
format!("std::cout << {} << std::endl;", a.join("<<"))
}
BuiltinFunction::Mod => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::fmod({}, {})", a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::Round => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::round({})", a.next().unwrap())
}
BuiltinFunction::Ceil => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::ceil({})", a.next().unwrap())
}
BuiltinFunction::Floor => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::floor({})", a.next().unwrap())
}
BuiltinFunction::Sqrt => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::sqrt({})", a.next().unwrap())
}
BuiltinFunction::Abs => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::abs({})", a.next().unwrap())
}
BuiltinFunction::Log => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::log({}) / std::log({})", a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::Pow => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::pow(({}), ({}))", a.next().unwrap(), a.next().unwrap())
}
BuiltinFunction::Sin => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::sin(({}) * {})", a.next().unwrap(), pi_180)
}
BuiltinFunction::Cos => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::cos(({}) * {})", a.next().unwrap(), pi_180)
}
BuiltinFunction::Tan => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::tan(({}) * {})", a.next().unwrap(), pi_180)
}
BuiltinFunction::ASin => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::asin({}) / {}", a.next().unwrap(), pi_180)
}
BuiltinFunction::ACos => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::acos({}) / {}", a.next().unwrap(), pi_180)
}
BuiltinFunction::ATan => {
ctx.generator_state.conditional_includes.cmath.set(true);
format!("std::atan({}) / {}", a.next().unwrap(), pi_180)
}
BuiltinFunction::SetFocusItem => {
Expand Down
9 changes: 6 additions & 3 deletions internal/compiler/generator/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1987,6 +1987,9 @@ fn compile_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream
Expression::Cast { from, to } => {
let f = compile_expression(from, ctx);
match (from.ty(ctx), to) {
(Type::Float32, Type::Int32) => {
quote!(((#f as f32).round() as i32))
}
(from, Type::String) if from.as_unit_product().is_some() => {
quote!(sp::SharedString::from(sp::format!("{}", #f).as_str()))
}
Expand Down Expand Up @@ -2645,9 +2648,9 @@ fn compile_builtin_function_call(
let (r, g, b, a) =
(a.next().unwrap(), a.next().unwrap(), a.next().unwrap(), a.next().unwrap());
quote!({
let r: u8 = (#r as u32).max(0).min(255) as u8;
let g: u8 = (#g as u32).max(0).min(255) as u8;
let b: u8 = (#b as u32).max(0).min(255) as u8;
let r: u8 = (#r as u32).min(255) as u8;
let g: u8 = (#g as u32).min(255) as u8;
let b: u8 = (#b as u32).min(255) as u8;
let a: u8 = (255. * (#a as f32)).max(0.).min(255.) as u8;
sp::Color::from_argb_u8(a, r, g, b)
})
Expand Down
42 changes: 42 additions & 0 deletions tests/cases/types/int_conversion.slint
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

export component TestCase {

property <float> float-var: 26.7;
// Going through a private property of type int should really cast to int
private property <int> tens-digit-var: float-var / 10;

// test that it rounds
out property <int> int-val: -7.6;

out property <string> text: tens-digit-var;
out property <bool> test: tens-digit-var == 3 && text == "3" && int-val == -8;
}


/*
```cpp
auto handle = TestCase::create();
const TestCase &instance = *handle;
assert_eq(instance.get_text(), "3");
assert_eq(instance.get_int_val(), -8);
assert(instance.get_test());
```
```rust
let instance = TestCase::new().unwrap();
assert_eq!(instance.get_text(), "3");
assert_eq!(instance.get_int_val(), -8);
assert!(instance.get_test());
```
```js
var instance = new slint.TestCase({});
assert.equal(instance.text, "3");
assert.equal(instance.int_val, -8);
assert(instance.test);
```
*/

0 comments on commit f5d003d

Please sign in to comment.