@@ -194,6 +194,147 @@ pub fn i64_rem_u(lhs: i64, rhs: i64) -> WasmResult<i64> {
194194 . ok_or ( WasmTrap :: DivisionByZero )
195195}
196196
197+ // ── Wasm float min/max/nearest ────────────────────────────────────────────────
198+
199+ /// Wasm `f32.min`: propagates NaN (unlike Rust's `f32::min` which ignores it).
200+ /// Also preserves the Wasm rule `min(-0.0, +0.0) = -0.0`.
201+ pub fn wasm_min_f32 ( a : f32 , b : f32 ) -> f32 {
202+ if a. is_nan ( ) || b. is_nan ( ) {
203+ return f32:: NAN ;
204+ }
205+ if a == 0.0 && b == 0.0 {
206+ return if a. is_sign_negative ( ) { a } else { b } ;
207+ }
208+ if a <= b {
209+ a
210+ } else {
211+ b
212+ }
213+ }
214+
215+ /// Wasm `f32.max`: propagates NaN. `max(-0.0, +0.0) = +0.0`.
216+ pub fn wasm_max_f32 ( a : f32 , b : f32 ) -> f32 {
217+ if a. is_nan ( ) || b. is_nan ( ) {
218+ return f32:: NAN ;
219+ }
220+ if a == 0.0 && b == 0.0 {
221+ return if a. is_sign_positive ( ) { a } else { b } ;
222+ }
223+ if a >= b {
224+ a
225+ } else {
226+ b
227+ }
228+ }
229+
230+ /// Wasm `f64.min`: propagates NaN. `min(-0.0, +0.0) = -0.0`.
231+ pub fn wasm_min_f64 ( a : f64 , b : f64 ) -> f64 {
232+ if a. is_nan ( ) || b. is_nan ( ) {
233+ return f64:: NAN ;
234+ }
235+ if a == 0.0 && b == 0.0 {
236+ return if a. is_sign_negative ( ) { a } else { b } ;
237+ }
238+ if a <= b {
239+ a
240+ } else {
241+ b
242+ }
243+ }
244+
245+ /// Wasm `f64.max`: propagates NaN. `max(-0.0, +0.0) = +0.0`.
246+ pub fn wasm_max_f64 ( a : f64 , b : f64 ) -> f64 {
247+ if a. is_nan ( ) || b. is_nan ( ) {
248+ return f64:: NAN ;
249+ }
250+ if a == 0.0 && b == 0.0 {
251+ return if a. is_sign_positive ( ) { a } else { b } ;
252+ }
253+ if a >= b {
254+ a
255+ } else {
256+ b
257+ }
258+ }
259+
260+ /// Wasm `f32.nearest` — round to nearest even (banker's rounding).
261+ ///
262+ /// Uses `as i32` for truncation-toward-zero (safe since we guard against values >= 2^23,
263+ /// which have no fractional bits). Avoids `f32::round`/`f32::trunc`
264+ /// which are not available in `no_std` without `libm`.
265+ pub fn wasm_nearest_f32 ( v : f32 ) -> f32 {
266+ if v. is_nan ( ) || v. is_infinite ( ) || v == 0.0 {
267+ return v;
268+ }
269+ // Floats >= 2^23 have no fractional bits — already an integer.
270+ const NO_FRAC : f32 = 8_388_608.0 ; // 2^23
271+ if v >= NO_FRAC || v <= -NO_FRAC {
272+ return v;
273+ }
274+ let trunc_i = v as i32 ; // truncates toward zero; safe since |v| < 2^23
275+ let trunc_f = trunc_i as f32 ;
276+ let frac = v - trunc_f; // in (-1.0, 1.0), same sign as v
277+ if frac > 0.5 {
278+ ( trunc_i + 1 ) as f32
279+ } else if frac < -0.5 {
280+ ( trunc_i - 1 ) as f32
281+ } else if frac == 0.5 {
282+ // Tie: round to even (trunc_i is the floor for positive v).
283+ if trunc_i % 2 == 0 {
284+ trunc_f
285+ } else {
286+ ( trunc_i + 1 ) as f32
287+ }
288+ } else if frac == -0.5 {
289+ // Tie: round to even. copysign preserves -0.0 when trunc_i == 0.
290+ if trunc_i % 2 == 0 {
291+ f32:: copysign ( trunc_f, v)
292+ } else {
293+ ( trunc_i - 1 ) as f32
294+ }
295+ } else {
296+ trunc_f
297+ }
298+ }
299+
300+ /// Wasm `f64.nearest` — round to nearest even (banker's rounding).
301+ ///
302+ /// Uses `as i64` for truncation-toward-zero (safe since we guard against values >= 2^52,
303+ /// which have no fractional bits). Avoids `f64::round`/`f64::trunc`
304+ /// which are not available in `no_std` without `libm`.
305+ pub fn wasm_nearest_f64 ( v : f64 ) -> f64 {
306+ if v. is_nan ( ) || v. is_infinite ( ) || v == 0.0 {
307+ return v;
308+ }
309+ // Floats >= 2^52 have no fractional bits — already an integer.
310+ const NO_FRAC : f64 = 4_503_599_627_370_496.0 ; // 2^52
311+ if v >= NO_FRAC || v <= -NO_FRAC {
312+ return v;
313+ }
314+ let trunc_i = v as i64 ; // truncates toward zero; safe since |v| < 2^52
315+ let trunc_f = trunc_i as f64 ;
316+ let frac = v - trunc_f;
317+ if frac > 0.5 {
318+ ( trunc_i + 1 ) as f64
319+ } else if frac < -0.5 {
320+ ( trunc_i - 1 ) as f64
321+ } else if frac == 0.5 {
322+ if trunc_i % 2 == 0 {
323+ trunc_f
324+ } else {
325+ ( trunc_i + 1 ) as f64
326+ }
327+ } else if frac == -0.5 {
328+ if trunc_i % 2 == 0 {
329+ f64:: copysign ( trunc_f, v)
330+ } else {
331+ ( trunc_i - 1 ) as f64
332+ }
333+ } else {
334+ trunc_f
335+ }
336+ }
337+
197338// ── Tests ─────────────────────────────────────────────────────────────────────
198339
199340#[ cfg( test) ]
0 commit comments