Skip to content

Commit

Permalink
Add: trunc/round/ceil/floor for Double (#408)
Browse files Browse the repository at this point in the history
* add trunc/round/ceil/floor to Double

* moon info fix

* add test cases

---------

Co-authored-by: linxw <linxw@silvrr.com>
  • Loading branch information
hiawui and linxw committed May 16, 2024
1 parent d2a9ac4 commit bbc5433
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 0 deletions.
107 changes: 107 additions & 0 deletions double/double.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ let min_val : Double = 0x1L.reinterpret_as_double()

let min_norm : Double = 0x0010000000000000L.reinterpret_as_double()

let sign_mask : Int64 = 0x8000_0000_0000_0000L

pub fn Double::from_int(i : Int) -> Double {
i.to_double()
}
Expand All @@ -36,6 +38,111 @@ pub fn abs(self : Double) -> Double {
}
}

pub fn trunc(self : Double) -> Double {
let u64 = self.reinterpret_as_i64()
let biased_exp = u64.lsr(52).land(0x7ffL).to_int()
if biased_exp < 1023 {
return u64.land(sign_mask).reinterpret_as_double()
}
if biased_exp >= 1075 {
return self
}
u64.land(sign_mask.asr(biased_exp - 1012)).reinterpret_as_double()
}

test "trunc" {
@assertion.assert_eq(4503599627370497.0.trunc(), 4503599627370497.0)?
@assertion.assert_eq(2251799813685248.5.trunc(), 2251799813685248.0)?
@assertion.assert_eq(4.5.trunc(), 4.0)?
@assertion.assert_eq(1.5.trunc(), 1.0)?
@assertion.assert_eq(1.0.trunc(), 1.0)?
@assertion.assert_eq(0.2.trunc(), 0.0)?
@assertion.assert_eq(0.0.trunc(), 0.0)?
@assertion.assert_eq((-0.0).trunc().reinterpret_as_i64(), sign_mask)?
@assertion.assert_eq((-0.2).trunc().reinterpret_as_i64(), sign_mask)?
@assertion.assert_eq((-1.0).trunc(), -1.0)?
@assertion.assert_eq((-1.5).trunc(), -1.0)?
@assertion.assert_eq((-2251799813685248.5).trunc(), -2251799813685248.0)?
@assertion.assert_eq((-4503599627370497.0).trunc(), -4503599627370497.0)?
}

pub fn ceil(self : Double) -> Double {
let trunced = self.trunc()
if self == trunced || self < 0.0 {
trunced
} else {
trunced + 1.0
}
}

test "ceil" {
@assertion.assert_eq(2251799813685248.5.ceil(), 2251799813685249.0)?
@assertion.assert_eq(1.1.ceil(), 2.0)?
@assertion.assert_eq(1.0.ceil(), 1.0)?
@assertion.assert_eq(0.9.ceil(), 1.0)?
@assertion.assert_eq(0.0.ceil(), 0.0)?
@assertion.assert_eq((-0.0).ceil().reinterpret_as_i64(), sign_mask)?
@assertion.assert_eq((-0.1).ceil().reinterpret_as_i64(), sign_mask)?
@assertion.assert_eq((-2251799813685248.5).ceil(), -2251799813685248.0)?
}

pub fn floor(self : Double) -> Double {
let trunced = self.trunc()
if self == trunced || self > 0.0 {
trunced
} else {
trunced - 1.0
}
}

test "floor" {
@assertion.assert_eq(2251799813685248.5.floor(), 2251799813685248.0)?
@assertion.assert_eq(1.1.floor(), 1.0)?
@assertion.assert_eq(1.0.floor(), 1.0)?
@assertion.assert_eq(0.9.floor(), 0.0)?
@assertion.assert_eq(0.0.floor(), 0.0)?
@assertion.assert_eq((-0.0).floor().reinterpret_as_i64(), sign_mask)?
@assertion.assert_eq((-0.1).floor(), -1.0)?
@assertion.assert_eq((-2251799813685248.5).floor(), -2251799813685249.0)?
}

pub fn round(self : Double) -> Double {
let u64 = self.reinterpret_as_i64()
let biased_exp = u64.lsr(52).land(0x7ffL).to_int()
if biased_exp >= 1075 {
return self
} else if biased_exp < 1022 {
return u64.land(sign_mask).reinterpret_as_double()
} else if biased_exp == 1022 {
return u64.land(sign_mask).lor(0x3FF0_0000_0000_0000L).reinterpret_as_double()
}
let trunced = u64.land(sign_mask.asr(biased_exp - 1012)).reinterpret_as_double()
if u64.land(sign_mask.lsr(biased_exp - 1011)) == 0L {
return trunced
} else if self > 0.0 {
return trunced + 1.0
} else {
return trunced - 1.0
}
}

test "round" {
@assertion.assert_eq(4503599627370497.5.round(), 4503599627370498.0)?
@assertion.assert_eq(2251799813685248.5.round(), 2251799813685249.0)?
@assertion.assert_eq(5.5.round(), 6.0)?
@assertion.assert_eq(5.2.round(), 5.0)?
@assertion.assert_eq(1.1.round(), 1.0)?
@assertion.assert_eq(1.0.round(), 1.0)?
@assertion.assert_eq(0.9.round(), 1.0)?
@assertion.assert_eq(0.5.round(), 1.0)?
@assertion.assert_eq(0.4.round(), 0.0)?
@assertion.assert_eq(0.0.round(), 0.0)?
@assertion.assert_eq((-0.0).round().reinterpret_as_i64(), sign_mask)?
@assertion.assert_eq((-0.1).round().reinterpret_as_i64(), sign_mask)?
@assertion.assert_eq((-0.5).round(), -1.0)?
@assertion.assert_eq((-2251799813685248.5).round(), -2251799813685249.0)?
}

/// Returns the sign of the double.
/// - If the double is positive, returns 1.0.
/// - If the double is negative, returns -1.0.
Expand Down
4 changes: 4 additions & 0 deletions double/double.mbti
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ package moonbitlang/core/double
// Types and methods
impl Double {
abs(Double) -> Double
ceil(Double) -> Double
debug_write(Double, Buffer) -> Unit
floor(Double) -> Double
from_int(Int) -> Double
hash(Double) -> Int
inf(Int) -> Double
Expand All @@ -17,8 +19,10 @@ impl Double {
min_normal() -> Double
min_value() -> Double
nan() -> Double
round(Double) -> Double
signum(Double) -> Double
to_string(Double) -> String
trunc(Double) -> Double
}

// Traits
Expand Down

0 comments on commit bbc5433

Please sign in to comment.