From 5c3dc30dc00d9505cb9d28f3eda053575cb8b7d2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 20 Dec 2018 10:54:56 +0000 Subject: [PATCH] More correct floor and ceil procedures. --- core/math/math.odin | 108 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/core/math/math.odin b/core/math/math.odin index 9e38c6549..5b24f0c0d 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -108,12 +108,38 @@ round_f32 :: proc(x: f32) -> f32 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5 round_f64 :: proc(x: f64) -> f64 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); } round :: proc{round_f32, round_f64}; -floor_f32 :: proc(x: f32) -> f32 { return x >= 0 ? f32(i64(x)) : f32(i64(x-0.5)); } // TODO: Get accurate versions -floor_f64 :: proc(x: f64) -> f64 { return x >= 0 ? f64(i64(x)) : f64(i64(x-0.5)); } // TODO: Get accurate versions +floor_f32 :: proc(x: f32) -> f32 { + if x == 0 || is_nan(x) || is_inf(x) { + return x; + } + if x < 0 { + d, fract := modf(-x); + if fract != 0.0 { + d = d + 1; + } + return -d; + } + d, _ := modf(x); + return d; +} +floor_f64 :: proc(x: f64) -> f64 { + if x == 0 || is_nan(x) || is_inf(x) { + return x; + } + if x < 0 { + d, fract := modf(-x); + if fract != 0.0 { + d = d + 1; + } + return -d; + } + d, _ := modf(x); + return d; +} floor :: proc{floor_f32, floor_f64}; -ceil_f32 :: proc(x: f32) -> f32 { return x < 0 ? f32(i64(x)) : f32(i64(x+1)); }// TODO: Get accurate versions -ceil_f64 :: proc(x: f64) -> f64 { return x < 0 ? f64(i64(x)) : f64(i64(x+1)); }// TODO: Get accurate versions +ceil_f32 :: proc(x: f32) -> f32 { return -floor_f32(-x); } +ceil_f64 :: proc(x: f64) -> f64 { return -floor_f64(-x); } ceil :: proc{ceil_f32, ceil_f64}; remainder_f32 :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; } @@ -140,6 +166,80 @@ mod_f64 :: proc(x, y: f64) -> f64 { } mod :: proc{mod_f32, mod_f64}; +// TODO(bill): These need to implemented with the actual instructions +modf_f32 :: proc(x: f32) -> (int: f32, frac: f32) { + shift :: 32 - 8 - 1; + mask :: 0xff; + bias :: 127; + + if x < 1 { + switch { + case x < 0: + int, frac = modf(-x); + return -int, -frac; + case x == 0: + return x, x; + } + return 0, x; + } + + i := transmute(u32)x; + e := uint(i>>shift)&mask - bias; + + if e < 32-12 { + i &~= 1<<(32-12-e) - 1; + } + int = transmute(f32)i; + frac = x - int; + return; +} +modf_f64 :: proc(x: f64) -> (int: f64, frac: f64) { + shift :: 64 - 11 - 1; + mask :: 0x7ff; + bias :: 1023; + + if x < 1 { + switch { + case x < 0: + int, frac = modf(-x); + return -int, -frac; + case x == 0: + return x, x; + } + return 0, x; + } + + i := transmute(u64)x; + e := uint(i>>shift)&mask - bias; + + if e < 64-12 { + i &~= 1<<(64-12-e) - 1; + } + int = transmute(f64)i; + frac = x - int; + return; +} +modf :: proc{modf_f32, modf_f64}; + +is_nan_f32 :: inline proc(x: f32) -> bool { return x != x; } +is_nan_f64 :: inline proc(x: f64) -> bool { return x != x; } +is_nan :: proc{is_nan_f32, is_nan_f64}; + +is_finite_f32 :: inline proc(x: f32) -> bool { return !is_nan(x-x); } +is_finite_f64 :: inline proc(x: f64) -> bool { return !is_nan(x-x); } +is_finite :: proc{is_finite_f32, is_finite_f64}; + +is_inf_f32 :: proc(x: f32, sign := 0) -> bool { + return sign >= 0 && x > F32_MAX || sign <= 0 && x < -F32_MAX; +} +is_inf_f64 :: proc(x: f64, sign := 0) -> bool { + return sign >= 0 && x > F64_MAX || sign <= 0 && x < -F64_MAX; +} +// If sign > 0, is_inf reports whether f is positive infinity +// If sign < 0, is_inf reports whether f is negative infinity +// If sign == 0, is_inf reports whether f is either infinity +is_inf :: proc{is_inf_f32, is_inf_f64}; + to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360; }