diff --git a/compiler/src/llvm/passes.rs b/compiler/src/llvm/passes.rs index 6a320277f..5b9d9c4d7 100644 --- a/compiler/src/llvm/passes.rs +++ b/compiler/src/llvm/passes.rs @@ -1061,6 +1061,48 @@ impl<'a, 'b, 'ctx> LowerMethod<'a, 'b, 'ctx> { self.builder.store(reg_var, res); } + BuiltinFunction::FloatRound => { + let reg_var = self.variables[&ins.register]; + let val_var = self.variables[&ins.arguments[0]]; + let val = self.read_float(val_var); + let func = self.module.intrinsic( + "llvm.round", + &[self.builder.context.f64_type().into()], + ); + + let raw = self + .builder + .call(func, &[val.into()]) + .into_float_value(); + + let res = self.new_float(state_var, raw); + + self.builder.store(reg_var, res); + } + BuiltinFunction::FloatPowi => { + let reg_var = self.variables[&ins.register]; + let lhs_var = self.variables[&ins.arguments[0]]; + let rhs_var = self.variables[&ins.arguments[1]]; + let lhs = self.read_float(lhs_var); + let raw_rhs = self.read_int(rhs_var); + let rhs = self.builder.int_to_int(raw_rhs, 32); + let func = self.module.intrinsic( + "llvm.powi", + &[ + self.builder.context.f64_type().into(), + self.builder.context.i32_type().into(), + ], + ); + + let raw = self + .builder + .call(func, &[lhs.into(), rhs.into()]) + .into_float_value(); + + let res = self.new_float(state_var, raw); + + self.builder.store(reg_var, res); + } BuiltinFunction::IntRotateLeft => { let reg_var = self.variables[&ins.register]; let lhs_var = self.variables[&ins.arguments[0]]; diff --git a/rt/src/runtime/float.rs b/rt/src/runtime/float.rs index c3ac40c79..4211f7d04 100644 --- a/rt/src/runtime/float.rs +++ b/rt/src/runtime/float.rs @@ -31,33 +31,6 @@ pub unsafe extern "system" fn inko_float_clone( } } -#[no_mangle] -pub unsafe extern "system" fn inko_float_round( - state: *const State, - float: f64, - precision: i64, -) -> *const Float { - let result = if precision == 0 { - float.round() - } else if precision <= i64::from(u32::MAX) { - let power = 10.0_f64.powi(precision as i32); - let multiplied = float * power; - - // Certain very large numbers (e.g. f64::MAX) would produce Infinity - // when multiplied with the power. In this case we just return the input - // float directly. - if multiplied.is_finite() { - multiplied.round() / power - } else { - float - } - } else { - float - }; - - Float::alloc((*state).float_class, result) -} - #[no_mangle] pub unsafe extern "system" fn inko_float_to_string( state: *const State, diff --git a/std/src/std/float.inko b/std/src/std/float.inko index 255bcf6c5..7413b8c45 100644 --- a/std/src/std/float.inko +++ b/std/src/std/float.inko @@ -4,7 +4,7 @@ import std::cmp::(Compare, Equal, Ordering) import std::fmt::(Format, Formatter) import std::hash::(Hash, Hasher) import std::int::(MAX, MIN, ToInt) -import std::ops::(Add, Divide, Modulo, Multiply, Subtract) +import std::ops::(Add, Divide, Modulo, Multiply, Power, Subtract) import std::string::ToString class extern AnyResult { @@ -12,12 +12,6 @@ class extern AnyResult { let @value: Any } -fn extern inko_float_round( - state: Pointer[Int8], - value: Float64, - decimals: Int, -) -> Float - fn extern inko_float_to_string(state: Pointer[Int8], float: Float64) -> String fn extern inko_string_to_float( state: Pointer[Int8], @@ -191,7 +185,13 @@ class builtin Float { # # Float.not_a_number.round.not_a_number? # => true fn pub round(decimals: Int) -> Float { - inko_float_round(_INKO.state, self as Float64, decimals) + if decimals <= 0 { return _INKO.float_round(self) } + if decimals > 4_294_967_295 { return self } + + let pow = 10.0 ** decimals + let mul = self * pow + + if mul.infinite? { self } else { _INKO.float_round(mul) / pow } } # Returns the fractional part of this float. @@ -385,3 +385,9 @@ impl Format for Float { formatter.write(to_string) } } + +impl Power[Int, Float] for Float { + fn pub **(other: ref Int) -> Float { + _INKO.float_powi(self, other) + } +} diff --git a/std/test/std/test_float.inko b/std/test/std/test_float.inko index 070034c26..84c6bbf92 100644 --- a/std/test/std/test_float.inko +++ b/std/test/std/test_float.inko @@ -1,6 +1,7 @@ import helpers::(fmt, hash) import std::cmp::Ordering import std::hash::Hasher +import std::int::(MAX as INT_MAX) import std::test::Tests let NAN = 0.0 / 0.0 @@ -99,9 +100,16 @@ fn pub tests(t: mut Tests) { } t.test('Float.round') fn (t) { + t.equal(10.123.round(0), 10.0) t.equal(10.123.round(1), 10.1) t.equal(10.123.round(2), 10.12) t.equal(10.123.round(3), 10.123) + t.equal(10.123.round(6), 10.123) + t.equal(10.123.round(INT_MAX), 10.123) + t.equal(10.123.round(-3), 10.0) + t.equal(Float.negative_infinity.round(3), Float.negative_infinity) + t.equal(Float.infinity.round(3), Float.infinity) + t.true(Float.not_a_number.round(3).not_a_number?) } t.test('Float.fractional') fn (t) { @@ -287,4 +295,13 @@ fn pub tests(t: mut Tests) { t.false(-0.0.positive_sign?) t.false(Float.negative_infinity.positive_sign?) } + + t.test('Float.**') fn (t) { + t.equal(1.2 ** 2, 1.44) + t.equal(1.2 ** 0, 1.0) + t.equal(10.0 ** 2, 100.0) + t.equal(-1.2 ** 2, 1.44) + t.equal(-1.2 ** 0, 1.0) + t.equal(-10.0 ** 2, 100.0) + } } diff --git a/types/src/lib.rs b/types/src/lib.rs index 1e35423bd..58d9ca080 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -1551,6 +1551,8 @@ pub enum BuiltinFunction { StringConcat, State, Process, + FloatRound, + FloatPowi, } impl BuiltinFunction { @@ -1599,6 +1601,8 @@ impl BuiltinFunction { BuiltinFunction::StringConcat, BuiltinFunction::State, BuiltinFunction::Process, + BuiltinFunction::FloatRound, + BuiltinFunction::FloatPowi, ] .into_iter() .fold(HashMap::new(), |mut map, func| { @@ -1652,6 +1656,8 @@ impl BuiltinFunction { BuiltinFunction::StringConcat => "string_concat", BuiltinFunction::State => "state", BuiltinFunction::Process => "process", + BuiltinFunction::FloatRound => "float_round", + BuiltinFunction::FloatPowi => "float_powi", } } @@ -1704,6 +1710,8 @@ impl BuiltinFunction { BuiltinFunction::Process => { TypeRef::pointer(TypeId::Foreign(ForeignType::Int(8))) } + BuiltinFunction::FloatRound => TypeRef::float(), + BuiltinFunction::FloatPowi => TypeRef::float(), } } }