nanvm_lib/big_numbers/
big_float.rs

1use std::{cmp::Ordering, ops::Deref};
2
3use crate::{
4    js::js_bigint::{
5        div_mod, from_u64, is_zero, mul, pow_u64, shl, shl_on_u64, shr_on_u64, zero, JsBigint,
6        JsBigintMutRef, Sign,
7    },
8    mem::manager::Manager,
9};
10
11#[derive(Debug)]
12pub struct BigFloat<const BASE: u32, M: Manager> {
13    pub manager: M,
14    pub significand: JsBigintMutRef<M::Dealloc>,
15    pub sign: Sign,
16    pub exp: i64,
17    pub non_zero_reminder: bool,
18}
19
20pub fn float_zero<const BASE: u32, M: Manager>(manager: M) -> BigFloat<BASE, M> {
21    BigFloat {
22        manager,
23        significand: zero(manager),
24        sign: Sign::Positive,
25        exp: 0,
26        non_zero_reminder: false,
27    }
28}
29
30impl<const BASE: u32, M: Manager> BigFloat<BASE, M> {
31    fn increase_significand(&mut self, precision: u64) {
32        if is_zero(self.significand.deref()) {
33            return;
34        }
35
36        let min_significand = shl(
37            self.manager,
38            from_u64(self.manager, Sign::Positive, 1).deref(),
39            from_u64(self.manager, Sign::Positive, precision).deref(),
40        );
41        self.increase_significand_to(min_significand.deref());
42    }
43
44    fn increase_significand_to(&mut self, min_significand: &JsBigint) {
45        if is_zero(self.significand.deref()) {
46            return;
47        }
48
49        loop {
50            match self.significand.deref().compare(min_significand) {
51                Ordering::Greater | Ordering::Equal => return,
52                _ => {}
53            }
54            self.significand = shl_on_u64(self.manager, self.significand.deref(), 1);
55            self.exp -= 1;
56        }
57    }
58
59    fn decrease_significand(&mut self, precision: u64) {
60        if is_zero(self.significand.deref()) {
61            return;
62        }
63
64        let max_significand = shl(
65            self.manager,
66            from_u64(self.manager, Sign::Positive, 1).deref(),
67            from_u64(self.manager, Sign::Positive, precision).deref(),
68        );
69        loop {
70            if self.significand.deref().compare(max_significand.deref()) == Ordering::Less {
71                break;
72            }
73            let last_bit = self.significand.get_last_bit();
74            if last_bit == 1 {
75                self.non_zero_reminder = true;
76            }
77            self.significand = shr_on_u64(self.manager, self.significand.deref(), 1);
78            self.exp += 1;
79        }
80    }
81}
82
83impl<M: Manager> BigFloat<10, M> {
84    pub fn to_bin(self, precision: u8) -> BigFloat<2, M> {
85        if is_zero(self.significand.deref()) {
86            return float_zero(self.manager);
87        }
88
89        if self.exp == 0 {
90            let mut result: BigFloat<2, M> = BigFloat {
91                manager: self.manager,
92                significand: self.significand,
93                sign: self.sign,
94                exp: self.exp,
95                non_zero_reminder: self.non_zero_reminder,
96            };
97            result.increase_significand(precision as u64);
98            result.decrease_significand(precision as u64);
99            return result;
100        }
101
102        let five = from_u64(self.manager, Sign::Positive, 5);
103        if self.exp > 0 {
104            let new_sign = mul(
105                self.manager,
106                self.significand.deref(),
107                pow_u64(self.manager, five.deref(), self.exp as u64).deref(),
108            );
109            let mut result: BigFloat<2, M> = BigFloat {
110                manager: self.manager,
111                significand: new_sign,
112                sign: self.sign,
113                exp: self.exp,
114                non_zero_reminder: self.non_zero_reminder,
115            };
116            result.increase_significand(precision as u64);
117            result.decrease_significand(precision as u64);
118            return result;
119        }
120
121        let p = pow_u64(self.manager, five.deref(), -self.exp as u64);
122        let mut bf10: BigFloat<10, M> = BigFloat {
123            manager: self.manager,
124            significand: self.significand,
125            sign: self.sign,
126            exp: self.exp,
127            non_zero_reminder: self.non_zero_reminder,
128        };
129        let min_significand = shl(
130            self.manager,
131            from_u64(self.manager, Sign::Positive, 1).deref(),
132            from_u64(self.manager, Sign::Positive, precision as u64).deref(),
133        );
134        bf10.increase_significand_to(
135            (mul(self.manager, p.deref(), min_significand.deref())).deref(),
136        );
137
138        let (q, r) = div_mod(self.manager, bf10.significand.deref(), p.deref());
139        let mut result: BigFloat<2, M> = BigFloat {
140            manager: self.manager,
141            significand: q,
142            sign: self.sign,
143            exp: bf10.exp,
144            non_zero_reminder: self.non_zero_reminder || !is_zero(r.deref()),
145        };
146        result.decrease_significand(precision as u64);
147        result
148    }
149}
150
151impl<M: Manager> BigFloat<2, M> {
152    fn get_frac_round(self) -> u64 {
153        let mut last_bit = self.significand.get_last_bit();
154        let mut frac = self.significand.items()[0] >> 1;
155
156        if last_bit == 1 && !self.non_zero_reminder {
157            last_bit = frac & 1;
158        }
159
160        if last_bit == 1 {
161            frac += 1;
162        }
163
164        frac
165    }
166
167    pub fn to_f64(self) -> f64 {
168        f64::from_bits(self.get_f64_bits())
169    }
170
171    fn get_f64_bits(self) -> u64 {
172        const PRECISION: u64 = 52;
173        const MAX_FRAC: u64 = 1 << (PRECISION + 1);
174        const FRAC_MASK: u64 = (1 << PRECISION) - 1;
175        const INF_BITS: u64 = 2047 << 52;
176
177        let mut bits: u64 = 0;
178        if self.sign == Sign::Negative {
179            bits |= 1 << 63;
180        }
181
182        if is_zero(self.significand.deref()) {
183            return bits;
184        }
185
186        let mut value = BigFloat {
187            manager: self.manager,
188            significand: self.significand,
189            sign: self.sign,
190            exp: self.exp,
191            non_zero_reminder: self.non_zero_reminder,
192        };
193        value.increase_significand(PRECISION + 1);
194        value.decrease_significand(PRECISION + 2);
195
196        let mut f64_exp = value.exp + PRECISION as i64 + 1;
197        match f64_exp {
198            -1022..=1023 => {
199                let mut frac = value.get_frac_round();
200                if frac == MAX_FRAC {
201                    frac >>= 1;
202                    f64_exp += 1;
203                    //if f64_exp equals 1024, then exp_bits will be all ones and frac_bits will be all zeros
204                    //it is an infinity by the f64 standard
205                }
206
207                let exp_bits = (f64_exp + 1023) as u64;
208                bits |= exp_bits << 52;
209                let frac_bits = frac & FRAC_MASK;
210                bits |= frac_bits;
211                bits
212            }
213            -1074..=-1023 => {
214                let subnormal_precision = (f64_exp + 1076) as u64;
215                value.decrease_significand(subnormal_precision);
216                let frac = value.get_frac_round();
217                //if frac equals 1 << 53, then exp_bits will be 1 and frac_bits will be all zeros
218                //it is a normal number by the f64 standard and it is the correct number
219                bits |= frac;
220                bits
221            }
222            exp if exp > 1023 => {
223                bits |= INF_BITS;
224                bits
225            }
226            _ => bits,
227        }
228    }
229}
230
231#[cfg(test)]
232mod test {
233    use wasm_bindgen_test::wasm_bindgen_test;
234
235    use crate::{
236        big_numbers::big_float::float_zero,
237        js::{
238            any::Any,
239            js_bigint::{from_u64, new_bigint, zero, JsBigintRef, Sign},
240            type_::Type,
241        },
242        mem::global::{Global, GLOBAL},
243    };
244
245    use super::BigFloat;
246
247    #[test]
248    #[wasm_bindgen_test]
249    fn test_zero() {
250        type A = Any<Global>;
251        type BigintRef = JsBigintRef<Global>;
252
253        let res = float_zero(GLOBAL).to_bin(64);
254        let any = A::move_from(res.significand.to_ref());
255        assert_eq!(any.get_type(), Type::Bigint);
256        {
257            let o = any.try_move::<BigintRef>().unwrap();
258            assert_eq!(o.sign(), Sign::Positive);
259            assert!(o.items().is_empty());
260        }
261        assert_eq!(res.exp, 0);
262        assert!(!res.non_zero_reminder);
263
264        let res = BigFloat {
265            manager: GLOBAL,
266            significand: zero(GLOBAL),
267            sign: Sign::Positive,
268            exp: 10,
269            non_zero_reminder: false,
270        }
271        .to_bin(64);
272        let any = A::move_from(res.significand.to_ref());
273        assert_eq!(any.get_type(), Type::Bigint);
274        {
275            let o = any.try_move::<BigintRef>().unwrap();
276            assert_eq!(o.sign(), Sign::Positive);
277            assert!(o.items().is_empty());
278        }
279        assert_eq!(res.exp, 0);
280        assert!(!res.non_zero_reminder);
281
282        let res = BigFloat {
283            manager: GLOBAL,
284            significand: zero(GLOBAL),
285            sign: Sign::Positive,
286            exp: -10,
287            non_zero_reminder: false,
288        }
289        .to_bin(64);
290        let any = A::move_from(res.significand.to_ref());
291        assert_eq!(any.get_type(), Type::Bigint);
292        {
293            let o = any.try_move::<BigintRef>().unwrap();
294            assert_eq!(o.sign(), Sign::Positive);
295            assert!(o.items().is_empty());
296        }
297        assert_eq!(res.exp, 0);
298        assert!(!res.non_zero_reminder);
299    }
300
301    #[test]
302    #[wasm_bindgen_test]
303    fn test_integer() {
304        type A = Any<Global>;
305        type BigintRef = JsBigintRef<Global>;
306
307        let res = BigFloat {
308            manager: GLOBAL,
309            significand: from_u64(GLOBAL, Sign::Positive, 100),
310            sign: Sign::Positive,
311            exp: 0,
312            non_zero_reminder: false,
313        }
314        .to_bin(7);
315        let any = A::move_from(res.significand.to_ref());
316        assert_eq!(any.get_type(), Type::Bigint);
317        {
318            let o = any.try_move::<BigintRef>().unwrap();
319            assert_eq!(o.sign(), Sign::Positive);
320            assert_eq!(o.items(), &[100]);
321        }
322        assert_eq!(res.exp, 0);
323        assert!(!res.non_zero_reminder);
324
325        let res = BigFloat {
326            manager: GLOBAL,
327            significand: from_u64(GLOBAL, Sign::Positive, 1),
328            sign: Sign::Positive,
329            exp: 1,
330            non_zero_reminder: false,
331        }
332        .to_bin(64);
333        let any = A::move_from(res.significand.to_ref());
334        assert_eq!(any.get_type(), Type::Bigint);
335        {
336            let o = any.try_move::<BigintRef>().unwrap();
337            assert_eq!(o.sign(), Sign::Positive);
338            assert_eq!(o.items(), &[10 << 60]);
339        }
340        assert_eq!(res.exp, -60);
341        assert!(!res.non_zero_reminder);
342
343        let res = BigFloat {
344            manager: GLOBAL,
345            significand: from_u64(GLOBAL, Sign::Positive, 100),
346            sign: Sign::Positive,
347            exp: 2,
348            non_zero_reminder: false,
349        }
350        .to_bin(64);
351        let any = A::move_from(res.significand.to_ref());
352        assert_eq!(any.get_type(), Type::Bigint);
353        {
354            let o = any.try_move::<BigintRef>().unwrap();
355            assert_eq!(o.sign(), Sign::Positive);
356            assert_eq!(o.items(), &[10000 << 50]);
357        }
358        assert_eq!(res.exp, -50);
359        assert!(!res.non_zero_reminder);
360
361        let res = BigFloat {
362            manager: GLOBAL,
363            significand: from_u64(GLOBAL, Sign::Positive, 128),
364            sign: Sign::Positive,
365            exp: 0,
366            non_zero_reminder: false,
367        }
368        .to_bin(9);
369        let any = A::move_from(res.significand.to_ref());
370        assert_eq!(any.get_type(), Type::Bigint);
371        {
372            let o = any.try_move::<BigintRef>().unwrap();
373            assert_eq!(o.sign(), Sign::Positive);
374            assert_eq!(o.items(), &[256]);
375        }
376        assert_eq!(res.exp, -1);
377        assert!(!res.non_zero_reminder);
378    }
379
380    #[test]
381    #[wasm_bindgen_test]
382    fn test_integer_rounding() {
383        type A = Any<Global>;
384        type BigintRef = JsBigintRef<Global>;
385
386        let res = BigFloat {
387            manager: GLOBAL,
388            significand: from_u64(GLOBAL, Sign::Positive, 128),
389            sign: Sign::Positive,
390            exp: 0,
391            non_zero_reminder: false,
392        }
393        .to_bin(4);
394        let any = A::move_from(res.significand.to_ref());
395        assert_eq!(any.get_type(), Type::Bigint);
396        {
397            let o = any.try_move::<BigintRef>().unwrap();
398            assert_eq!(o.sign(), Sign::Positive);
399            assert_eq!(o.items(), &[8]);
400        }
401        assert_eq!(res.exp, 4);
402        assert!(!res.non_zero_reminder);
403
404        let res = BigFloat {
405            manager: GLOBAL,
406            significand: from_u64(GLOBAL, Sign::Positive, 129),
407            sign: Sign::Positive,
408            exp: 0,
409            non_zero_reminder: false,
410        }
411        .to_bin(4);
412        let any = A::move_from(res.significand.to_ref());
413        assert_eq!(any.get_type(), Type::Bigint);
414        {
415            let o = any.try_move::<BigintRef>().unwrap();
416            assert_eq!(o.sign(), Sign::Positive);
417            assert_eq!(o.items(), &[8]);
418        }
419        assert_eq!(res.exp, 4);
420        assert!(res.non_zero_reminder);
421    }
422
423    #[test]
424    #[wasm_bindgen_test]
425    fn test_float() {
426        type A = Any<Global>;
427        type BigintRef = JsBigintRef<Global>;
428
429        let res = BigFloat {
430            manager: GLOBAL,
431            significand: from_u64(GLOBAL, Sign::Positive, 100),
432            sign: Sign::Positive,
433            exp: -1,
434            non_zero_reminder: false,
435        }
436        .to_bin(5);
437        let any = A::move_from(res.significand.to_ref());
438        assert_eq!(any.get_type(), Type::Bigint);
439        {
440            let o = any.try_move::<BigintRef>().unwrap();
441            assert_eq!(o.sign(), Sign::Positive);
442            assert_eq!(o.items(), &[20]);
443        }
444        assert_eq!(res.exp, -1);
445        assert!(!res.non_zero_reminder);
446
447        let res = BigFloat {
448            manager: GLOBAL,
449            significand: from_u64(GLOBAL, Sign::Positive, 100),
450            sign: Sign::Positive,
451            exp: -1,
452            non_zero_reminder: false,
453        }
454        .to_bin(64);
455        let any = A::move_from(res.significand.to_ref());
456        assert_eq!(any.get_type(), Type::Bigint);
457        {
458            let o = any.try_move::<BigintRef>().unwrap();
459            assert_eq!(o.sign(), Sign::Positive);
460            assert_eq!(o.items(), &[(1 << 63) + (1 << 61)]);
461        }
462        assert_eq!(res.exp, -60);
463        assert!(!res.non_zero_reminder);
464
465        let res = BigFloat {
466            manager: GLOBAL,
467            significand: new_bigint(GLOBAL, Sign::Positive, [0, 1]),
468            sign: Sign::Positive,
469            exp: 0,
470            non_zero_reminder: false,
471        }
472        .to_bin(53);
473        let any = A::move_from(res.significand.to_ref());
474        assert_eq!(any.get_type(), Type::Bigint);
475        {
476            let o = any.try_move::<BigintRef>().unwrap();
477            assert_eq!(o.sign(), Sign::Positive);
478            assert_eq!(o.items(), &[1 << 52]);
479        }
480        assert_eq!(res.exp, 12);
481        assert!(!res.non_zero_reminder);
482    }
483
484    #[test]
485    #[wasm_bindgen_test]
486    fn test_rounding() {
487        type A = Any<Global>;
488        type BigintRef = JsBigintRef<Global>;
489
490        let res = BigFloat {
491            manager: GLOBAL,
492            significand: from_u64(GLOBAL, Sign::Positive, 0b1000_0001),
493            sign: Sign::Positive,
494            exp: -1,
495            non_zero_reminder: false,
496        }
497        .to_bin(5);
498        let any = A::move_from(res.significand.to_ref());
499        assert_eq!(any.get_type(), Type::Bigint);
500        {
501            let o = any.try_move::<BigintRef>().unwrap();
502            assert_eq!(o.sign(), Sign::Positive);
503            assert_eq!(o.items(), &[0b11001]);
504        }
505        assert_eq!(res.exp, -1);
506        assert!(res.non_zero_reminder);
507
508        let res = BigFloat {
509            manager: GLOBAL,
510            significand: from_u64(GLOBAL, Sign::Positive, 0b1000_0001),
511            sign: Sign::Positive,
512            exp: -1,
513            non_zero_reminder: false,
514        }
515        .to_bin(4);
516        let any = A::move_from(res.significand.to_ref());
517        assert_eq!(any.get_type(), Type::Bigint);
518        {
519            let o = any.try_move::<BigintRef>().unwrap();
520            assert_eq!(o.sign(), Sign::Positive);
521            assert_eq!(o.items(), &[0b1100]);
522        }
523        assert_eq!(res.exp, 0);
524        assert!(res.non_zero_reminder);
525
526        let res = BigFloat {
527            manager: GLOBAL,
528            significand: from_u64(GLOBAL, Sign::Positive, 0b1000_0001),
529            sign: Sign::Positive,
530            exp: -1,
531            non_zero_reminder: false,
532        }
533        .to_bin(3);
534        let any = A::move_from(res.significand.to_ref());
535        assert_eq!(any.get_type(), Type::Bigint);
536        {
537            let o = any.try_move::<BigintRef>().unwrap();
538            assert_eq!(o.sign(), Sign::Positive);
539            assert_eq!(o.items(), &[0b110]);
540        }
541        assert_eq!(res.exp, 1);
542        assert!(res.non_zero_reminder);
543    }
544
545    #[test]
546    #[wasm_bindgen_test]
547    fn test_rounding_half() {
548        type A = Any<Global>;
549        type BigintRef = JsBigintRef<Global>;
550
551        let res = BigFloat {
552            manager: GLOBAL,
553            significand: from_u64(GLOBAL, Sign::Positive, 0b101_1010),
554            sign: Sign::Positive,
555            exp: -1,
556            non_zero_reminder: false,
557        }
558        .to_bin(3);
559        let any = A::move_from(res.significand.to_ref());
560        assert_eq!(any.get_type(), Type::Bigint);
561        {
562            let o = any.try_move::<BigintRef>().unwrap();
563            assert_eq!(o.sign(), Sign::Positive);
564            assert_eq!(o.items(), &[0b100]);
565        }
566        assert_eq!(res.exp, 1);
567        assert!(res.non_zero_reminder);
568
569        let res = BigFloat {
570            manager: GLOBAL,
571            significand: from_u64(GLOBAL, Sign::Positive, 0b101_1011),
572            sign: Sign::Positive,
573            exp: -1,
574            non_zero_reminder: false,
575        }
576        .to_bin(3);
577        let any = A::move_from(res.significand.to_ref());
578        assert_eq!(any.get_type(), Type::Bigint);
579        {
580            let o = any.try_move::<BigintRef>().unwrap();
581            assert_eq!(o.sign(), Sign::Positive);
582            assert_eq!(o.items(), &[0b100]);
583        }
584        assert_eq!(res.exp, 1);
585        assert!(res.non_zero_reminder);
586
587        let res = BigFloat {
588            manager: GLOBAL,
589            significand: from_u64(GLOBAL, Sign::Positive, 0b110_1101),
590            sign: Sign::Positive,
591            exp: -1,
592            non_zero_reminder: false,
593        }
594        .to_bin(3);
595        let any = A::move_from(res.significand.to_ref());
596        assert_eq!(any.get_type(), Type::Bigint);
597        {
598            let o = any.try_move::<BigintRef>().unwrap();
599            assert_eq!(o.sign(), Sign::Positive);
600            assert_eq!(o.items(), &[0b101]);
601        }
602        assert_eq!(res.exp, 1);
603        assert!(res.non_zero_reminder);
604
605        let res = BigFloat {
606            manager: GLOBAL,
607            significand: from_u64(GLOBAL, Sign::Positive, 0b110_1110),
608            sign: Sign::Positive,
609            exp: -1,
610            non_zero_reminder: false,
611        }
612        .to_bin(3);
613        let any = A::move_from(res.significand.to_ref());
614        assert_eq!(any.get_type(), Type::Bigint);
615        {
616            let o = any.try_move::<BigintRef>().unwrap();
617            assert_eq!(o.sign(), Sign::Positive);
618            assert_eq!(o.items(), &[0b101]);
619        }
620        assert_eq!(res.exp, 1);
621        assert!(res.non_zero_reminder);
622
623        let res = BigFloat {
624            manager: GLOBAL,
625            significand: from_u64(GLOBAL, Sign::Positive, 0b1001_0110),
626            sign: Sign::Positive,
627            exp: -1,
628            non_zero_reminder: false,
629        }
630        .to_bin(3);
631        let any = A::move_from(res.significand.to_ref());
632        assert_eq!(any.get_type(), Type::Bigint);
633        {
634            let o = any.try_move::<BigintRef>().unwrap();
635            assert_eq!(o.sign(), Sign::Positive);
636            assert_eq!(o.items(), &[0b111]);
637        }
638        assert_eq!(res.exp, 1);
639        assert!(res.non_zero_reminder);
640    }
641
642    #[test]
643    #[wasm_bindgen_test]
644    fn test_zero_to_f64() {
645        type A = Any<Global>;
646        type BigintRef = JsBigintRef<Global>;
647
648        let res = BigFloat {
649            manager: GLOBAL,
650            significand: zero(GLOBAL),
651            sign: Sign::Positive,
652            exp: 100,
653            non_zero_reminder: false,
654        }
655        .to_f64();
656        assert_eq!(res, 0.0);
657        assert!(res.is_sign_positive());
658
659        let res = BigFloat {
660            manager: GLOBAL,
661            significand: zero(GLOBAL),
662            sign: Sign::Negative,
663            exp: 100,
664            non_zero_reminder: false,
665        }
666        .to_f64();
667        assert_eq!(res, 0.0);
668        assert!(res.is_sign_negative());
669    }
670
671    #[test]
672    #[wasm_bindgen_test]
673    fn test_normal_to_f64() {
674        type A = Any<Global>;
675        type BigintRef = JsBigintRef<Global>;
676
677        let res = BigFloat {
678            manager: GLOBAL,
679            significand: from_u64(GLOBAL, Sign::Positive, 1),
680            sign: Sign::Positive,
681            exp: 0,
682            non_zero_reminder: false,
683        }
684        .to_f64();
685        assert_eq!(res, 1.0);
686
687        let res = BigFloat {
688            manager: GLOBAL,
689            significand: from_u64(GLOBAL, Sign::Positive, 3),
690            sign: Sign::Negative,
691            exp: -1,
692            non_zero_reminder: false,
693        }
694        .to_f64();
695        assert_eq!(res, -1.5);
696
697        let res = BigFloat {
698            manager: GLOBAL,
699            significand: from_u64(GLOBAL, Sign::Positive, 1),
700            sign: Sign::Positive,
701            exp: -1022,
702            non_zero_reminder: false,
703        }
704        .to_f64();
705        assert_eq!(res, 2.0f64.powf(-1022.0));
706        assert!(res.is_normal());
707
708        let res = BigFloat {
709            manager: GLOBAL,
710            significand: from_u64(GLOBAL, Sign::Positive, 1 << 59),
711            sign: Sign::Positive,
712            exp: -1022 - 59,
713            non_zero_reminder: false,
714        }
715        .to_f64();
716        assert_eq!(res, 2.0f64.powf(-1022.0));
717        assert!(res.is_normal());
718
719        let res = BigFloat {
720            manager: GLOBAL,
721            significand: from_u64(GLOBAL, Sign::Positive, (1 << 60) - 1),
722            sign: Sign::Positive,
723            exp: -1022 - 60,
724            non_zero_reminder: false,
725        }
726        .to_f64();
727        assert_eq!(res, 2.0f64.powf(-1022.0));
728        assert!(res.is_normal());
729
730        let res = BigFloat {
731            manager: GLOBAL,
732            significand: from_u64(GLOBAL, Sign::Positive, 1),
733            sign: Sign::Positive,
734            exp: 1023,
735            non_zero_reminder: false,
736        }
737        .to_f64();
738        assert_eq!(res, 2.0f64.powf(1023.0));
739
740        let res = BigFloat {
741            manager: GLOBAL,
742            significand: from_u64(GLOBAL, Sign::Positive, (1 << 52) - 1),
743            sign: Sign::Positive,
744            exp: 0,
745            non_zero_reminder: false,
746        }
747        .to_f64();
748        assert_eq!(res, 4503599627370495f64);
749
750        let res = BigFloat {
751            manager: GLOBAL,
752            significand: from_u64(GLOBAL, Sign::Positive, (1 << 53) - 1),
753            sign: Sign::Positive,
754            exp: 0,
755            non_zero_reminder: false,
756        }
757        .to_f64();
758        assert_eq!(res, 9007199254740991f64);
759    }
760
761    #[test]
762    #[wasm_bindgen_test]
763    fn test_normal_to_f64_rounding() {
764        type A = Any<Global>;
765        type BigintRef = JsBigintRef<Global>;
766
767        let res = BigFloat {
768            manager: GLOBAL,
769            significand: from_u64(GLOBAL, Sign::Positive, (1 << 54) - 1), //111111111111111111111111111111111111111111111111111111
770            sign: Sign::Positive,
771            exp: 0,
772            non_zero_reminder: false,
773        }
774        .to_f64();
775        assert_eq!(res, 18014398509481984f64);
776
777        let res = BigFloat {
778            manager: GLOBAL,
779            significand: from_u64(GLOBAL, Sign::Positive, (1 << 54) - 2), //111111111111111111111111111111111111111111111111111110
780            sign: Sign::Positive,
781            exp: 0,
782            non_zero_reminder: false,
783        }
784        .to_f64();
785        assert_eq!(res, 18014398509481982f64);
786
787        let res = BigFloat {
788            manager: GLOBAL,
789            significand: from_u64(GLOBAL, Sign::Positive, (1 << 54) - 3), //111111111111111111111111111111111111111111111111111101
790            sign: Sign::Positive,
791            exp: 0,
792            non_zero_reminder: true,
793        }
794        .to_f64();
795        assert_eq!(res, 18014398509481982f64);
796
797        let res = BigFloat {
798            manager: GLOBAL,
799            significand: from_u64(GLOBAL, Sign::Positive, (1 << 54) - 3), //111111111111111111111111111111111111111111111111111101
800            sign: Sign::Positive,
801            exp: 0,
802            non_zero_reminder: false,
803        }
804        .to_f64();
805        assert_eq!(res, 18014398509481980f64);
806
807        let res = BigFloat {
808            manager: GLOBAL,
809            significand: from_u64(GLOBAL, Sign::Positive, (1 << 54) - 1),
810            sign: Sign::Positive,
811            exp: 969,
812            non_zero_reminder: false,
813        }
814        .to_f64();
815        assert!(res.is_normal());
816
817        let res = BigFloat {
818            manager: GLOBAL,
819            significand: from_u64(GLOBAL, Sign::Positive, (1 << 54) - 1),
820            sign: Sign::Positive,
821            exp: 970,
822            non_zero_reminder: false,
823        }
824        .to_f64();
825        assert!(res.is_infinite());
826    }
827
828    #[test]
829    #[wasm_bindgen_test]
830    fn test_infinity_to_f64() {
831        type A = Any<Global>;
832        type BigintRef = JsBigintRef<Global>;
833
834        let res = BigFloat {
835            manager: GLOBAL,
836            significand: from_u64(GLOBAL, Sign::Positive, 1),
837            sign: Sign::Positive,
838            exp: 1024,
839            non_zero_reminder: false,
840        }
841        .to_f64();
842        assert!(res.is_infinite());
843        assert!(res.is_sign_positive());
844
845        let res = BigFloat {
846            manager: GLOBAL,
847            significand: from_u64(GLOBAL, Sign::Positive, 1),
848            sign: Sign::Negative,
849            exp: 1024,
850            non_zero_reminder: false,
851        }
852        .to_f64();
853        assert!(res.is_infinite());
854        assert!(res.is_sign_negative());
855    }
856
857    #[test]
858    #[wasm_bindgen_test]
859    fn test_subnormal_to_f64() {
860        type A = Any<Global>;
861        type BigintRef = JsBigintRef<Global>;
862
863        let res = BigFloat {
864            manager: GLOBAL,
865            significand: from_u64(GLOBAL, Sign::Positive, 1),
866            sign: Sign::Positive,
867            exp: -1023,
868            non_zero_reminder: false,
869        }
870        .to_f64();
871        assert_eq!(res, 2.0f64.powf(-1023.0));
872        assert!(res.is_subnormal());
873
874        let res = BigFloat {
875            manager: GLOBAL,
876            significand: from_u64(GLOBAL, Sign::Positive, 1),
877            sign: Sign::Negative,
878            exp: -1023,
879            non_zero_reminder: false,
880        }
881        .to_f64();
882        assert_eq!(res, -(2.0f64.powf(-1023.0)));
883        assert!(res.is_subnormal());
884
885        let res = BigFloat {
886            manager: GLOBAL,
887            significand: from_u64(GLOBAL, Sign::Positive, 1),
888            sign: Sign::Positive,
889            exp: -1074,
890            non_zero_reminder: false,
891        }
892        .to_f64();
893        assert_eq!(res.to_bits(), 1);
894        assert_eq!(res, 2.0f64.powf(-1074.0));
895        assert!(res.is_subnormal());
896
897        let res = BigFloat {
898            manager: GLOBAL,
899            significand: from_u64(GLOBAL, Sign::Positive, 1),
900            sign: Sign::Positive,
901            exp: -1075,
902            non_zero_reminder: false,
903        }
904        .to_f64();
905        assert_eq!(res, 0.0);
906    }
907
908    #[test]
909    #[wasm_bindgen_test]
910    fn test_subnormal_to_f64_rounding() {
911        type A = Any<Global>;
912        type BigintRef = JsBigintRef<Global>;
913
914        //     0.0 => 0
915        let res = BigFloat {
916            manager: GLOBAL,
917            significand: from_u64(GLOBAL, Sign::Positive, 0b100),
918            sign: Sign::Positive,
919            exp: -1075,
920            non_zero_reminder: false,
921        }
922        .to_f64();
923        assert_eq!(res.to_bits(), 0b10);
924        assert_eq!(res, 2.0f64.powf(-1073.0));
925        assert!(res.is_subnormal());
926
927        //     0.0+ => 0
928        let res = BigFloat {
929            manager: GLOBAL,
930            significand: from_u64(GLOBAL, Sign::Positive, 0b100),
931            sign: Sign::Positive,
932            exp: -1075,
933            non_zero_reminder: true,
934        }
935        .to_f64();
936        assert_eq!(res.to_bits(), 0b10);
937        assert_eq!(res, 2.0f64.powf(-1073.0));
938        assert!(res.is_subnormal());
939
940        //     0.1 => 0
941        let res = BigFloat {
942            manager: GLOBAL,
943            significand: from_u64(GLOBAL, Sign::Positive, 0b101),
944            sign: Sign::Positive,
945            exp: -1075,
946            non_zero_reminder: false,
947        }
948        .to_f64();
949        assert_eq!(res.to_bits(), 0b10);
950        assert_eq!(res, 2.0f64.powf(-1073.0));
951        assert!(res.is_subnormal());
952
953        //     0.1+ => 1
954        let res = BigFloat {
955            manager: GLOBAL,
956            significand: from_u64(GLOBAL, Sign::Positive, 0b101),
957            sign: Sign::Positive,
958            exp: -1075,
959            non_zero_reminder: true,
960        }
961        .to_f64();
962        assert_eq!(res.to_bits(), 0b11);
963        assert_eq!(res, 1.5f64 * 2.0f64.powf(-1073.0));
964        assert!(res.is_subnormal());
965
966        //     1.0 => 1
967        let res = BigFloat {
968            manager: GLOBAL,
969            significand: from_u64(GLOBAL, Sign::Positive, 0b110),
970            sign: Sign::Positive,
971            exp: -1075,
972            non_zero_reminder: false,
973        }
974        .to_f64();
975        assert_eq!(res.to_bits(), 0b11);
976        assert_eq!(res, 1.5f64 * 2.0f64.powf(-1073.0));
977        assert!(res.is_subnormal());
978
979        //     1.0+ => 1
980        let res = BigFloat {
981            manager: GLOBAL,
982            significand: from_u64(GLOBAL, Sign::Positive, 0b110),
983            sign: Sign::Positive,
984            exp: -1075,
985            non_zero_reminder: true,
986        }
987        .to_f64();
988        assert_eq!(res.to_bits(), 0b11);
989        assert_eq!(res, 1.5f64 * 2.0f64.powf(-1073.0));
990        assert!(res.is_subnormal());
991
992        //     1.1 => 2
993        let res = BigFloat {
994            manager: GLOBAL,
995            significand: from_u64(GLOBAL, Sign::Positive, 0b111),
996            sign: Sign::Positive,
997            exp: -1075,
998            non_zero_reminder: false,
999        }
1000        .to_f64();
1001        assert_eq!(res.to_bits(), 0b100);
1002        assert_eq!(res, 2.0f64.powf(-1072.0));
1003        assert!(res.is_subnormal());
1004
1005        //     1.1+ => 2
1006        let res = BigFloat {
1007            manager: GLOBAL,
1008            significand: from_u64(GLOBAL, Sign::Positive, 0b111),
1009            sign: Sign::Positive,
1010            exp: -1075,
1011            non_zero_reminder: true,
1012        }
1013        .to_f64();
1014        assert_eq!(res.to_bits(), 0b100);
1015        assert_eq!(res, 2.0f64.powf(-1072.0));
1016        assert!(res.is_subnormal());
1017    }
1018
1019    #[test]
1020    #[wasm_bindgen_test]
1021    fn test_rust_cast() {
1022        test(18014398509481981);
1023        test(18014398509481982);
1024        test(18014398509481983);
1025        test(18014398509481984);
1026        test(18014398509481985);
1027
1028        fn test(n: u64) {
1029            type A = Any<Global>;
1030            type BigintRef = JsBigintRef<Global>;
1031
1032            let res = BigFloat {
1033                manager: GLOBAL,
1034                significand: from_u64(GLOBAL, Sign::Positive, n),
1035                sign: Sign::Positive,
1036                exp: 0,
1037                non_zero_reminder: false,
1038            }
1039            .to_f64();
1040            assert_eq!(res, n as f64);
1041        }
1042    }
1043}